@revstackhq/core 0.0.0-dev-20260227103607 → 0.0.0-dev-20260228062053
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 +372 -118
- package/dist/index.js +179 -52
- package/package.json +4 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* @description Core type definitions for Revstack's Billing Engine.
|
|
4
|
-
* These types map directly to the PostgreSQL database schema and
|
|
5
|
-
* serve as the contract for "Billing as Code".
|
|
6
|
-
*/
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
7
3
|
/**
|
|
8
4
|
* The type of a feature/entitlement.
|
|
9
5
|
* - 'boolean': On/Off flag (e.g., SSO Access).
|
|
@@ -19,7 +15,7 @@ type UnitType = "count" | "bytes" | "seconds" | "tokens" | "requests" | "custom"
|
|
|
19
15
|
/**
|
|
20
16
|
* How often a feature's usage counter resets.
|
|
21
17
|
*/
|
|
22
|
-
type ResetPeriod = "monthly" | "yearly" | "never";
|
|
18
|
+
type ResetPeriod = "daily" | "weekly" | "monthly" | "yearly" | "never";
|
|
23
19
|
/**
|
|
24
20
|
* Billing interval for a plan's price.
|
|
25
21
|
*/
|
|
@@ -28,7 +24,7 @@ type BillingInterval = "monthly" | "quarterly" | "yearly" | "one_time";
|
|
|
28
24
|
* The commercial classification of a plan.
|
|
29
25
|
* - 'free': No payment required (e.g., Default Guest Plan, Starter).
|
|
30
26
|
* - 'paid': Requires active payment method.
|
|
31
|
-
* - 'custom': Enterprise / negotiated
|
|
27
|
+
* - 'custom': Enterprise / B2B negotiated contracts where there is no strict price array and the checkout should redirect to a "Contact Sales" flow.
|
|
32
28
|
*/
|
|
33
29
|
type PlanType = "paid" | "free" | "custom";
|
|
34
30
|
/**
|
|
@@ -131,6 +127,8 @@ interface PriceDef {
|
|
|
131
127
|
/** The quantity of units the overage_amount applies to. */
|
|
132
128
|
overage_unit: number;
|
|
133
129
|
}>;
|
|
130
|
+
/** Slugs of addons that can be attached to this specific price/interval. */
|
|
131
|
+
available_addons?: string[];
|
|
134
132
|
}
|
|
135
133
|
/**
|
|
136
134
|
* Full plan definition. Maps to the `plans` table in the database.
|
|
@@ -157,8 +155,6 @@ interface PlanDef {
|
|
|
157
155
|
prices?: PriceDef[];
|
|
158
156
|
/** Feature entitlements included in this plan. */
|
|
159
157
|
features: Record<string, PlanFeatureValue>;
|
|
160
|
-
/** Slugs of addons that can be attached to this plan. */
|
|
161
|
-
available_addons?: string[];
|
|
162
158
|
}
|
|
163
159
|
/**
|
|
164
160
|
* Input type for `definePlan()`.
|
|
@@ -168,7 +164,6 @@ interface PlanDef {
|
|
|
168
164
|
type PlanDefInput = Omit<PlanDef, "slug" | "status" | "features"> & {
|
|
169
165
|
status?: PlanStatus;
|
|
170
166
|
features: Record<string, PlanFeatureValue>;
|
|
171
|
-
available_addons?: string[];
|
|
172
167
|
};
|
|
173
168
|
/**
|
|
174
169
|
* An add-on is a product purchased on top of a subscription.
|
|
@@ -183,8 +178,12 @@ interface AddonDef {
|
|
|
183
178
|
description?: string;
|
|
184
179
|
/** Billing type. */
|
|
185
180
|
type: "recurring" | "one_time";
|
|
186
|
-
/**
|
|
187
|
-
|
|
181
|
+
/** Price amount in the smallest currency unit (e.g., cents). */
|
|
182
|
+
amount: number;
|
|
183
|
+
/** ISO 4217 currency code. */
|
|
184
|
+
currency: string;
|
|
185
|
+
/** Billing interval (Required if type === 'recurring'). */
|
|
186
|
+
billing_interval?: Exclude<BillingInterval, "one_time">;
|
|
188
187
|
/** Feature entitlements this add-on modifies or grants. */
|
|
189
188
|
features: Record<string, AddonFeatureValue>;
|
|
190
189
|
}
|
|
@@ -195,19 +194,11 @@ interface AddonDef {
|
|
|
195
194
|
type AddonDefInput = Omit<AddonDef, "slug">;
|
|
196
195
|
type DiscountType = "percent" | "amount";
|
|
197
196
|
type DiscountDuration = "once" | "forever" | "repeating";
|
|
198
|
-
interface
|
|
197
|
+
interface DiscountBase {
|
|
199
198
|
/** The code the user enters at checkout (e.g., 'BLACKFRIDAY_24'). */
|
|
200
199
|
code: string;
|
|
201
200
|
/** Friendly name for invoices. */
|
|
202
201
|
name?: string;
|
|
203
|
-
/** 'percent' (0–100) or 'amount' (smallest currency unit). */
|
|
204
|
-
type: DiscountType;
|
|
205
|
-
/** The discount value. */
|
|
206
|
-
value: number;
|
|
207
|
-
/** How long the discount lasts. */
|
|
208
|
-
duration: DiscountDuration;
|
|
209
|
-
/** If duration is 'repeating', how many months. */
|
|
210
|
-
duration_in_months?: number;
|
|
211
202
|
/** Restrict to specific plan slugs. Empty = all. */
|
|
212
203
|
applies_to_plans?: string[];
|
|
213
204
|
/** Maximum number of redemptions globally. */
|
|
@@ -215,6 +206,21 @@ interface DiscountDef {
|
|
|
215
206
|
/** Expiration date (ISO 8601). */
|
|
216
207
|
expires_at?: string;
|
|
217
208
|
}
|
|
209
|
+
type DiscountValueDef = {
|
|
210
|
+
type: "percent";
|
|
211
|
+
value: number;
|
|
212
|
+
} | {
|
|
213
|
+
type: "amount";
|
|
214
|
+
value: number;
|
|
215
|
+
};
|
|
216
|
+
type DiscountDurationDef = {
|
|
217
|
+
/** How long the discount lasts. */ duration: "once" | "forever";
|
|
218
|
+
duration_in_months?: never;
|
|
219
|
+
} | {
|
|
220
|
+
/** How long the discount lasts. */ duration: "repeating";
|
|
221
|
+
/** How many months. */ duration_in_months: number;
|
|
222
|
+
};
|
|
223
|
+
type DiscountDef = DiscountBase & DiscountValueDef & DiscountDurationDef;
|
|
218
224
|
/**
|
|
219
225
|
* The output of the Entitlement Engine.
|
|
220
226
|
* Answers: "Can the user do this?"
|
|
@@ -249,49 +255,6 @@ interface RevstackConfig {
|
|
|
249
255
|
coupons?: DiscountDef[];
|
|
250
256
|
}
|
|
251
257
|
|
|
252
|
-
/**
|
|
253
|
-
* @file define.ts
|
|
254
|
-
* @description Identity helpers for "Billing as Code" config authoring.
|
|
255
|
-
*
|
|
256
|
-
* These functions return their input unchanged — their sole purpose is to
|
|
257
|
-
* provide autocompletion, type narrowing, and compile-time validation
|
|
258
|
-
* when developers write their `revstack.config.ts`.
|
|
259
|
-
*
|
|
260
|
-
* @example
|
|
261
|
-
* ```typescript
|
|
262
|
-
* import { defineConfig, defineFeature, definePlan } from "@revstackhq/core";
|
|
263
|
-
*
|
|
264
|
-
* const features = {
|
|
265
|
-
* seats: defineFeature({ name: "Seats", type: "static", unit_type: "count" }),
|
|
266
|
-
* sso: defineFeature({ name: "SSO", type: "boolean", unit_type: "count" }),
|
|
267
|
-
* };
|
|
268
|
-
*
|
|
269
|
-
* export default defineConfig({
|
|
270
|
-
* features,
|
|
271
|
-
* plans: {
|
|
272
|
-
* default: definePlan<typeof features>({
|
|
273
|
-
* name: "Default",
|
|
274
|
-
* is_default: true,
|
|
275
|
-
* is_public: false,
|
|
276
|
-
* type: "free",
|
|
277
|
-
* features: {},
|
|
278
|
-
* }),
|
|
279
|
-
* pro: definePlan<typeof features>({
|
|
280
|
-
* name: "Pro",
|
|
281
|
-
* is_default: false,
|
|
282
|
-
* is_public: true,
|
|
283
|
-
* type: "paid",
|
|
284
|
-
* prices: [{ amount: 2900, currency: "USD", billing_interval: "monthly" }],
|
|
285
|
-
* features: {
|
|
286
|
-
* seats: { value_limit: 5, is_hard_limit: true },
|
|
287
|
-
* sso: { value_bool: true },
|
|
288
|
-
* },
|
|
289
|
-
* }),
|
|
290
|
-
* },
|
|
291
|
-
* });
|
|
292
|
-
* ```
|
|
293
|
-
*/
|
|
294
|
-
|
|
295
258
|
/**
|
|
296
259
|
* Define a feature with full type inference.
|
|
297
260
|
* Identity function — returns the input as-is.
|
|
@@ -332,17 +295,8 @@ declare function definePlan<F extends Record<string, FeatureDefInput> = Record<s
|
|
|
332
295
|
*
|
|
333
296
|
* @typeParam F - Feature dictionary type for key restriction.
|
|
334
297
|
*/
|
|
335
|
-
declare function defineAddon<F extends Record<string, FeatureDefInput> = Record<string, FeatureDefInput>>(config: Omit<AddonDefInput, "features"
|
|
298
|
+
declare function defineAddon<F extends Record<string, FeatureDefInput> = Record<string, FeatureDefInput>>(config: Omit<AddonDefInput, "features"> & {
|
|
336
299
|
features: F extends Record<string, FeatureDefInput> ? Partial<Record<keyof F, AddonFeatureValue>> : Record<string, AddonFeatureValue>;
|
|
337
|
-
prices?: Array<Omit<PriceDef, "overage_configuration"> & {
|
|
338
|
-
overage_configuration?: F extends Record<string, FeatureDefInput> ? Partial<Record<keyof F, {
|
|
339
|
-
overage_amount: number;
|
|
340
|
-
overage_unit: number;
|
|
341
|
-
}>> : Record<string, {
|
|
342
|
-
overage_amount: number;
|
|
343
|
-
overage_unit: number;
|
|
344
|
-
}>;
|
|
345
|
-
}>;
|
|
346
300
|
}): AddonDefInput;
|
|
347
301
|
/**
|
|
348
302
|
* Define a discount/coupon with full type inference.
|
|
@@ -355,28 +309,6 @@ declare function defineDiscount<T extends DiscountDef>(config: T): T;
|
|
|
355
309
|
*/
|
|
356
310
|
declare function defineConfig<T extends RevstackConfig>(config: T): T;
|
|
357
311
|
|
|
358
|
-
/**
|
|
359
|
-
* @file engine.ts
|
|
360
|
-
* @description The Entitlement Engine — the logic core of Revstack.
|
|
361
|
-
*
|
|
362
|
-
* Determines if a user can access a feature based on their active Plan,
|
|
363
|
-
* purchased Add-ons, and subscription payment status. All decisions are
|
|
364
|
-
* pure, stateless computations with no side effects.
|
|
365
|
-
*
|
|
366
|
-
* @example
|
|
367
|
-
* ```typescript
|
|
368
|
-
* import { EntitlementEngine } from "@revstackhq/core";
|
|
369
|
-
*
|
|
370
|
-
* const engine = new EntitlementEngine(plan, addons, "active");
|
|
371
|
-
*
|
|
372
|
-
* // Single check
|
|
373
|
-
* const result = engine.check("seats", 4);
|
|
374
|
-
*
|
|
375
|
-
* // Batch check
|
|
376
|
-
* const results = engine.checkBatch({ seats: 4, ai_tokens: 12000 });
|
|
377
|
-
* ```
|
|
378
|
-
*/
|
|
379
|
-
|
|
380
312
|
/**
|
|
381
313
|
* The Entitlement Engine — evaluates feature access for a single customer.
|
|
382
314
|
*
|
|
@@ -420,24 +352,6 @@ declare class EntitlementEngine {
|
|
|
420
352
|
checkBatch(usages: Record<string, number>): Record<string, CheckResult>;
|
|
421
353
|
}
|
|
422
354
|
|
|
423
|
-
/**
|
|
424
|
-
* @file validator.ts
|
|
425
|
-
* @description Runtime validation for Revstack billing configurations.
|
|
426
|
-
*
|
|
427
|
-
* Validates the business logic invariants of a `RevstackConfig` object
|
|
428
|
-
* before it is synced to the backend. Catches misconfigurations early
|
|
429
|
-
* that TypeScript's type system cannot enforce (e.g., referencing
|
|
430
|
-
* undefined features, negative prices, duplicate slugs).
|
|
431
|
-
*
|
|
432
|
-
* @example
|
|
433
|
-
* ```typescript
|
|
434
|
-
* import { validateConfig, defineConfig } from "@revstackhq/core";
|
|
435
|
-
*
|
|
436
|
-
* const config = defineConfig({ features: {}, plans: {} });
|
|
437
|
-
* validateConfig(config); // throws RevstackValidationError if invalid
|
|
438
|
-
* ```
|
|
439
|
-
*/
|
|
440
|
-
|
|
441
355
|
/**
|
|
442
356
|
* Thrown when `validateConfig()` detects one or more invalid business
|
|
443
357
|
* logic rules in a billing configuration.
|
|
@@ -457,12 +371,352 @@ declare class RevstackValidationError extends Error {
|
|
|
457
371
|
* Checks the following invariants:
|
|
458
372
|
* 1. **Default plan** — Exactly one plan has `is_default: true`.
|
|
459
373
|
* 2. **Feature references** — Plans/addons only reference features defined in `config.features`.
|
|
460
|
-
* 3. **Non-negative pricing** — All price amounts and limits are ≥ 0.
|
|
461
|
-
* 4. **Discount bounds** — Percentage discounts have values in [0, 100].
|
|
462
374
|
*
|
|
463
375
|
* @param config - The billing configuration to validate.
|
|
464
376
|
* @throws {RevstackValidationError} If any violations are found.
|
|
465
377
|
*/
|
|
466
378
|
declare function validateConfig(config: RevstackConfig): void;
|
|
467
379
|
|
|
468
|
-
|
|
380
|
+
declare const FeatureTypeSchema: z.ZodEnum<{
|
|
381
|
+
boolean: "boolean";
|
|
382
|
+
static: "static";
|
|
383
|
+
metered: "metered";
|
|
384
|
+
}>;
|
|
385
|
+
declare const UnitTypeSchema: z.ZodEnum<{
|
|
386
|
+
count: "count";
|
|
387
|
+
bytes: "bytes";
|
|
388
|
+
seconds: "seconds";
|
|
389
|
+
tokens: "tokens";
|
|
390
|
+
requests: "requests";
|
|
391
|
+
custom: "custom";
|
|
392
|
+
}>;
|
|
393
|
+
declare const ResetPeriodSchema: z.ZodEnum<{
|
|
394
|
+
daily: "daily";
|
|
395
|
+
weekly: "weekly";
|
|
396
|
+
monthly: "monthly";
|
|
397
|
+
yearly: "yearly";
|
|
398
|
+
never: "never";
|
|
399
|
+
}>;
|
|
400
|
+
declare const BillingIntervalSchema: z.ZodEnum<{
|
|
401
|
+
monthly: "monthly";
|
|
402
|
+
yearly: "yearly";
|
|
403
|
+
quarterly: "quarterly";
|
|
404
|
+
one_time: "one_time";
|
|
405
|
+
}>;
|
|
406
|
+
declare const PlanTypeSchema: z.ZodEnum<{
|
|
407
|
+
custom: "custom";
|
|
408
|
+
paid: "paid";
|
|
409
|
+
free: "free";
|
|
410
|
+
}>;
|
|
411
|
+
declare const PlanStatusSchema: z.ZodEnum<{
|
|
412
|
+
draft: "draft";
|
|
413
|
+
active: "active";
|
|
414
|
+
archived: "archived";
|
|
415
|
+
}>;
|
|
416
|
+
declare const SubscriptionStatusSchema: z.ZodEnum<{
|
|
417
|
+
active: "active";
|
|
418
|
+
trialing: "trialing";
|
|
419
|
+
past_due: "past_due";
|
|
420
|
+
canceled: "canceled";
|
|
421
|
+
paused: "paused";
|
|
422
|
+
}>;
|
|
423
|
+
declare const FeatureDefInputSchema: z.ZodObject<{
|
|
424
|
+
name: z.ZodString;
|
|
425
|
+
description: z.ZodOptional<z.ZodString>;
|
|
426
|
+
type: z.ZodEnum<{
|
|
427
|
+
boolean: "boolean";
|
|
428
|
+
static: "static";
|
|
429
|
+
metered: "metered";
|
|
430
|
+
}>;
|
|
431
|
+
unit_type: z.ZodEnum<{
|
|
432
|
+
count: "count";
|
|
433
|
+
bytes: "bytes";
|
|
434
|
+
seconds: "seconds";
|
|
435
|
+
tokens: "tokens";
|
|
436
|
+
requests: "requests";
|
|
437
|
+
custom: "custom";
|
|
438
|
+
}>;
|
|
439
|
+
}, z.core.$strip>;
|
|
440
|
+
declare const PlanFeatureValueSchema: z.ZodObject<{
|
|
441
|
+
value_limit: z.ZodOptional<z.ZodNumber>;
|
|
442
|
+
value_bool: z.ZodOptional<z.ZodBoolean>;
|
|
443
|
+
value_text: z.ZodOptional<z.ZodString>;
|
|
444
|
+
is_hard_limit: z.ZodOptional<z.ZodBoolean>;
|
|
445
|
+
reset_period: z.ZodOptional<z.ZodEnum<{
|
|
446
|
+
daily: "daily";
|
|
447
|
+
weekly: "weekly";
|
|
448
|
+
monthly: "monthly";
|
|
449
|
+
yearly: "yearly";
|
|
450
|
+
never: "never";
|
|
451
|
+
}>>;
|
|
452
|
+
}, z.core.$strip>;
|
|
453
|
+
declare const AddonFeatureValueSchema: z.ZodObject<{
|
|
454
|
+
value_limit: z.ZodOptional<z.ZodNumber>;
|
|
455
|
+
type: z.ZodOptional<z.ZodEnum<{
|
|
456
|
+
increment: "increment";
|
|
457
|
+
set: "set";
|
|
458
|
+
}>>;
|
|
459
|
+
has_access: z.ZodOptional<z.ZodBoolean>;
|
|
460
|
+
is_hard_limit: z.ZodOptional<z.ZodBoolean>;
|
|
461
|
+
}, z.core.$strip>;
|
|
462
|
+
declare const OverageConfigurationSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
463
|
+
overage_amount: z.ZodNumber;
|
|
464
|
+
overage_unit: z.ZodNumber;
|
|
465
|
+
}, z.core.$strip>>;
|
|
466
|
+
declare const PriceDefSchema: z.ZodObject<{
|
|
467
|
+
amount: z.ZodNumber;
|
|
468
|
+
currency: z.ZodString;
|
|
469
|
+
billing_interval: z.ZodEnum<{
|
|
470
|
+
monthly: "monthly";
|
|
471
|
+
yearly: "yearly";
|
|
472
|
+
quarterly: "quarterly";
|
|
473
|
+
one_time: "one_time";
|
|
474
|
+
}>;
|
|
475
|
+
trial_period_days: z.ZodOptional<z.ZodNumber>;
|
|
476
|
+
is_active: z.ZodOptional<z.ZodBoolean>;
|
|
477
|
+
overage_configuration: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
478
|
+
overage_amount: z.ZodNumber;
|
|
479
|
+
overage_unit: z.ZodNumber;
|
|
480
|
+
}, z.core.$strip>>>;
|
|
481
|
+
available_addons: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
482
|
+
}, z.core.$strip>;
|
|
483
|
+
declare const PlanDefInputSchema: z.ZodObject<{
|
|
484
|
+
name: z.ZodString;
|
|
485
|
+
description: z.ZodOptional<z.ZodString>;
|
|
486
|
+
is_default: z.ZodBoolean;
|
|
487
|
+
is_public: z.ZodBoolean;
|
|
488
|
+
type: z.ZodEnum<{
|
|
489
|
+
custom: "custom";
|
|
490
|
+
paid: "paid";
|
|
491
|
+
free: "free";
|
|
492
|
+
}>;
|
|
493
|
+
status: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
|
|
494
|
+
draft: "draft";
|
|
495
|
+
active: "active";
|
|
496
|
+
archived: "archived";
|
|
497
|
+
}>>>;
|
|
498
|
+
prices: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
499
|
+
amount: z.ZodNumber;
|
|
500
|
+
currency: z.ZodString;
|
|
501
|
+
billing_interval: z.ZodEnum<{
|
|
502
|
+
monthly: "monthly";
|
|
503
|
+
yearly: "yearly";
|
|
504
|
+
quarterly: "quarterly";
|
|
505
|
+
one_time: "one_time";
|
|
506
|
+
}>;
|
|
507
|
+
trial_period_days: z.ZodOptional<z.ZodNumber>;
|
|
508
|
+
is_active: z.ZodOptional<z.ZodBoolean>;
|
|
509
|
+
overage_configuration: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
510
|
+
overage_amount: z.ZodNumber;
|
|
511
|
+
overage_unit: z.ZodNumber;
|
|
512
|
+
}, z.core.$strip>>>;
|
|
513
|
+
available_addons: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
514
|
+
}, z.core.$strip>>>;
|
|
515
|
+
features: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
516
|
+
value_limit: z.ZodOptional<z.ZodNumber>;
|
|
517
|
+
value_bool: z.ZodOptional<z.ZodBoolean>;
|
|
518
|
+
value_text: z.ZodOptional<z.ZodString>;
|
|
519
|
+
is_hard_limit: z.ZodOptional<z.ZodBoolean>;
|
|
520
|
+
reset_period: z.ZodOptional<z.ZodEnum<{
|
|
521
|
+
daily: "daily";
|
|
522
|
+
weekly: "weekly";
|
|
523
|
+
monthly: "monthly";
|
|
524
|
+
yearly: "yearly";
|
|
525
|
+
never: "never";
|
|
526
|
+
}>>;
|
|
527
|
+
}, z.core.$strip>>;
|
|
528
|
+
}, z.core.$strip>;
|
|
529
|
+
declare const AddonDefInputSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
530
|
+
name: z.ZodString;
|
|
531
|
+
description: z.ZodOptional<z.ZodString>;
|
|
532
|
+
amount: z.ZodNumber;
|
|
533
|
+
currency: z.ZodString;
|
|
534
|
+
features: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
535
|
+
value_limit: z.ZodOptional<z.ZodNumber>;
|
|
536
|
+
type: z.ZodOptional<z.ZodEnum<{
|
|
537
|
+
increment: "increment";
|
|
538
|
+
set: "set";
|
|
539
|
+
}>>;
|
|
540
|
+
has_access: z.ZodOptional<z.ZodBoolean>;
|
|
541
|
+
is_hard_limit: z.ZodOptional<z.ZodBoolean>;
|
|
542
|
+
}, z.core.$strip>>;
|
|
543
|
+
type: z.ZodLiteral<"recurring">;
|
|
544
|
+
billing_interval: z.ZodEnum<{
|
|
545
|
+
monthly: "monthly";
|
|
546
|
+
yearly: "yearly";
|
|
547
|
+
quarterly: "quarterly";
|
|
548
|
+
}>;
|
|
549
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
550
|
+
name: z.ZodString;
|
|
551
|
+
description: z.ZodOptional<z.ZodString>;
|
|
552
|
+
amount: z.ZodNumber;
|
|
553
|
+
currency: z.ZodString;
|
|
554
|
+
features: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
555
|
+
value_limit: z.ZodOptional<z.ZodNumber>;
|
|
556
|
+
type: z.ZodOptional<z.ZodEnum<{
|
|
557
|
+
increment: "increment";
|
|
558
|
+
set: "set";
|
|
559
|
+
}>>;
|
|
560
|
+
has_access: z.ZodOptional<z.ZodBoolean>;
|
|
561
|
+
is_hard_limit: z.ZodOptional<z.ZodBoolean>;
|
|
562
|
+
}, z.core.$strip>>;
|
|
563
|
+
type: z.ZodLiteral<"one_time">;
|
|
564
|
+
billing_interval: z.ZodOptional<z.ZodAny>;
|
|
565
|
+
}, z.core.$strip>], "type">;
|
|
566
|
+
declare const DiscountTypeSchema: z.ZodEnum<{
|
|
567
|
+
amount: "amount";
|
|
568
|
+
percent: "percent";
|
|
569
|
+
}>;
|
|
570
|
+
declare const DiscountDurationSchema: z.ZodEnum<{
|
|
571
|
+
once: "once";
|
|
572
|
+
forever: "forever";
|
|
573
|
+
repeating: "repeating";
|
|
574
|
+
}>;
|
|
575
|
+
declare const DiscountDefSchema: z.ZodIntersection<z.ZodIntersection<z.ZodObject<{
|
|
576
|
+
code: z.ZodString;
|
|
577
|
+
name: z.ZodOptional<z.ZodString>;
|
|
578
|
+
applies_to_plans: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
579
|
+
max_redemptions: z.ZodOptional<z.ZodNumber>;
|
|
580
|
+
expires_at: z.ZodOptional<z.ZodString>;
|
|
581
|
+
}, z.core.$strip>, z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
582
|
+
type: z.ZodLiteral<"percent">;
|
|
583
|
+
value: z.ZodNumber;
|
|
584
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
585
|
+
type: z.ZodLiteral<"amount">;
|
|
586
|
+
value: z.ZodNumber;
|
|
587
|
+
}, z.core.$strip>], "type">>, z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
588
|
+
duration: z.ZodLiteral<"once">;
|
|
589
|
+
duration_in_months: z.ZodOptional<z.ZodUndefined>;
|
|
590
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
591
|
+
duration: z.ZodLiteral<"forever">;
|
|
592
|
+
duration_in_months: z.ZodOptional<z.ZodUndefined>;
|
|
593
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
594
|
+
duration: z.ZodLiteral<"repeating">;
|
|
595
|
+
duration_in_months: z.ZodNumber;
|
|
596
|
+
}, z.core.$strip>], "duration">>;
|
|
597
|
+
declare const RevstackConfigSchema: z.ZodObject<{
|
|
598
|
+
features: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
599
|
+
name: z.ZodString;
|
|
600
|
+
description: z.ZodOptional<z.ZodString>;
|
|
601
|
+
type: z.ZodEnum<{
|
|
602
|
+
boolean: "boolean";
|
|
603
|
+
static: "static";
|
|
604
|
+
metered: "metered";
|
|
605
|
+
}>;
|
|
606
|
+
unit_type: z.ZodEnum<{
|
|
607
|
+
count: "count";
|
|
608
|
+
bytes: "bytes";
|
|
609
|
+
seconds: "seconds";
|
|
610
|
+
tokens: "tokens";
|
|
611
|
+
requests: "requests";
|
|
612
|
+
custom: "custom";
|
|
613
|
+
}>;
|
|
614
|
+
}, z.core.$strip>>;
|
|
615
|
+
plans: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
616
|
+
name: z.ZodString;
|
|
617
|
+
description: z.ZodOptional<z.ZodString>;
|
|
618
|
+
is_default: z.ZodBoolean;
|
|
619
|
+
is_public: z.ZodBoolean;
|
|
620
|
+
type: z.ZodEnum<{
|
|
621
|
+
custom: "custom";
|
|
622
|
+
paid: "paid";
|
|
623
|
+
free: "free";
|
|
624
|
+
}>;
|
|
625
|
+
status: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
|
|
626
|
+
draft: "draft";
|
|
627
|
+
active: "active";
|
|
628
|
+
archived: "archived";
|
|
629
|
+
}>>>;
|
|
630
|
+
prices: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
631
|
+
amount: z.ZodNumber;
|
|
632
|
+
currency: z.ZodString;
|
|
633
|
+
billing_interval: z.ZodEnum<{
|
|
634
|
+
monthly: "monthly";
|
|
635
|
+
yearly: "yearly";
|
|
636
|
+
quarterly: "quarterly";
|
|
637
|
+
one_time: "one_time";
|
|
638
|
+
}>;
|
|
639
|
+
trial_period_days: z.ZodOptional<z.ZodNumber>;
|
|
640
|
+
is_active: z.ZodOptional<z.ZodBoolean>;
|
|
641
|
+
overage_configuration: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
642
|
+
overage_amount: z.ZodNumber;
|
|
643
|
+
overage_unit: z.ZodNumber;
|
|
644
|
+
}, z.core.$strip>>>;
|
|
645
|
+
available_addons: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
646
|
+
}, z.core.$strip>>>;
|
|
647
|
+
features: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
648
|
+
value_limit: z.ZodOptional<z.ZodNumber>;
|
|
649
|
+
value_bool: z.ZodOptional<z.ZodBoolean>;
|
|
650
|
+
value_text: z.ZodOptional<z.ZodString>;
|
|
651
|
+
is_hard_limit: z.ZodOptional<z.ZodBoolean>;
|
|
652
|
+
reset_period: z.ZodOptional<z.ZodEnum<{
|
|
653
|
+
daily: "daily";
|
|
654
|
+
weekly: "weekly";
|
|
655
|
+
monthly: "monthly";
|
|
656
|
+
yearly: "yearly";
|
|
657
|
+
never: "never";
|
|
658
|
+
}>>;
|
|
659
|
+
}, z.core.$strip>>;
|
|
660
|
+
}, z.core.$strip>>;
|
|
661
|
+
addons: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
662
|
+
name: z.ZodString;
|
|
663
|
+
description: z.ZodOptional<z.ZodString>;
|
|
664
|
+
amount: z.ZodNumber;
|
|
665
|
+
currency: z.ZodString;
|
|
666
|
+
features: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
667
|
+
value_limit: z.ZodOptional<z.ZodNumber>;
|
|
668
|
+
type: z.ZodOptional<z.ZodEnum<{
|
|
669
|
+
increment: "increment";
|
|
670
|
+
set: "set";
|
|
671
|
+
}>>;
|
|
672
|
+
has_access: z.ZodOptional<z.ZodBoolean>;
|
|
673
|
+
is_hard_limit: z.ZodOptional<z.ZodBoolean>;
|
|
674
|
+
}, z.core.$strip>>;
|
|
675
|
+
type: z.ZodLiteral<"recurring">;
|
|
676
|
+
billing_interval: z.ZodEnum<{
|
|
677
|
+
monthly: "monthly";
|
|
678
|
+
yearly: "yearly";
|
|
679
|
+
quarterly: "quarterly";
|
|
680
|
+
}>;
|
|
681
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
682
|
+
name: z.ZodString;
|
|
683
|
+
description: z.ZodOptional<z.ZodString>;
|
|
684
|
+
amount: z.ZodNumber;
|
|
685
|
+
currency: z.ZodString;
|
|
686
|
+
features: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
687
|
+
value_limit: z.ZodOptional<z.ZodNumber>;
|
|
688
|
+
type: z.ZodOptional<z.ZodEnum<{
|
|
689
|
+
increment: "increment";
|
|
690
|
+
set: "set";
|
|
691
|
+
}>>;
|
|
692
|
+
has_access: z.ZodOptional<z.ZodBoolean>;
|
|
693
|
+
is_hard_limit: z.ZodOptional<z.ZodBoolean>;
|
|
694
|
+
}, z.core.$strip>>;
|
|
695
|
+
type: z.ZodLiteral<"one_time">;
|
|
696
|
+
billing_interval: z.ZodOptional<z.ZodAny>;
|
|
697
|
+
}, z.core.$strip>], "type">>>;
|
|
698
|
+
coupons: z.ZodOptional<z.ZodArray<z.ZodIntersection<z.ZodIntersection<z.ZodObject<{
|
|
699
|
+
code: z.ZodString;
|
|
700
|
+
name: z.ZodOptional<z.ZodString>;
|
|
701
|
+
applies_to_plans: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
702
|
+
max_redemptions: z.ZodOptional<z.ZodNumber>;
|
|
703
|
+
expires_at: z.ZodOptional<z.ZodString>;
|
|
704
|
+
}, z.core.$strip>, z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
705
|
+
type: z.ZodLiteral<"percent">;
|
|
706
|
+
value: z.ZodNumber;
|
|
707
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
708
|
+
type: z.ZodLiteral<"amount">;
|
|
709
|
+
value: z.ZodNumber;
|
|
710
|
+
}, z.core.$strip>], "type">>, z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
711
|
+
duration: z.ZodLiteral<"once">;
|
|
712
|
+
duration_in_months: z.ZodOptional<z.ZodUndefined>;
|
|
713
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
714
|
+
duration: z.ZodLiteral<"forever">;
|
|
715
|
+
duration_in_months: z.ZodOptional<z.ZodUndefined>;
|
|
716
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
717
|
+
duration: z.ZodLiteral<"repeating">;
|
|
718
|
+
duration_in_months: z.ZodNumber;
|
|
719
|
+
}, z.core.$strip>], "duration">>>>;
|
|
720
|
+
}, z.core.$strip>;
|
|
721
|
+
|
|
722
|
+
export { type AddonDef, type AddonDefInput, AddonDefInputSchema, type AddonFeatureValue, AddonFeatureValueSchema, type BillingInterval, BillingIntervalSchema, type CheckResult, type DiscountBase, type DiscountDef, DiscountDefSchema, type DiscountDuration, type DiscountDurationDef, DiscountDurationSchema, type DiscountType, DiscountTypeSchema, type DiscountValueDef, EntitlementEngine, type FeatureDef, type FeatureDefInput, FeatureDefInputSchema, type FeatureType, FeatureTypeSchema, OverageConfigurationSchema, type PlanDef, type PlanDefInput, PlanDefInputSchema, type PlanFeatureValue, PlanFeatureValueSchema, type PlanStatus, PlanStatusSchema, type PlanType, PlanTypeSchema, type PriceDef, PriceDefSchema, type ResetPeriod, ResetPeriodSchema, type RevstackConfig, RevstackConfigSchema, RevstackValidationError, type SubscriptionStatus, SubscriptionStatusSchema, type UnitType, UnitTypeSchema, defineAddon, defineConfig, defineDiscount, defineFeature, definePlan, validateConfig };
|
package/dist/index.js
CHANGED
|
@@ -171,50 +171,43 @@ function validateFeatureReferences(productType, productSlug, features, knownFeat
|
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
173
|
}
|
|
174
|
-
function
|
|
174
|
+
function validatePriceAddonIntervals(planSlug, priceIndex, price, configAddons, errors) {
|
|
175
|
+
if (!price.available_addons) return;
|
|
176
|
+
for (const addonSlug of price.available_addons) {
|
|
177
|
+
const addon = configAddons?.[addonSlug];
|
|
178
|
+
if (!addon) {
|
|
179
|
+
errors.push(
|
|
180
|
+
`Plan "${planSlug}" price references undefined addon "${addonSlug}".`
|
|
181
|
+
);
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
if (addon.type === "recurring" && addon.billing_interval !== price.billing_interval) {
|
|
185
|
+
errors.push(
|
|
186
|
+
`Interval Mismatch: Plan '${planSlug}' price is '${price.billing_interval}', but Addon '${addonSlug}' is '${addon.billing_interval}'. Recurring addons must match the price's billing interval.`
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function validatePlanPricing(slug, prices, config, errors) {
|
|
192
|
+
const configFeatures = config.features;
|
|
175
193
|
if (prices) {
|
|
176
|
-
|
|
177
|
-
if (price.amount < 0) {
|
|
178
|
-
errors.push(
|
|
179
|
-
`${productType} "${slug}" has a negative price amount (${price.amount}).`
|
|
180
|
-
);
|
|
181
|
-
}
|
|
194
|
+
prices.forEach((price, index) => {
|
|
182
195
|
if (price.overage_configuration) {
|
|
183
|
-
for (const
|
|
184
|
-
price.overage_configuration
|
|
185
|
-
)) {
|
|
196
|
+
for (const featureSlug of Object.keys(price.overage_configuration)) {
|
|
186
197
|
const feature = configFeatures[featureSlug];
|
|
187
198
|
if (!feature) {
|
|
188
199
|
errors.push(
|
|
189
|
-
|
|
200
|
+
`Plan "${slug}" overage_configuration references undefined feature "${featureSlug}".`
|
|
190
201
|
);
|
|
191
202
|
} else if (feature.type !== "metered") {
|
|
192
203
|
errors.push(
|
|
193
|
-
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
if (overage.overage_amount < 0) {
|
|
197
|
-
errors.push(
|
|
198
|
-
`${productType} "${slug}" overage_amount for feature "${featureSlug}" must be >= 0.`
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
if (overage.overage_unit <= 0) {
|
|
202
|
-
errors.push(
|
|
203
|
-
`${productType} "${slug}" overage_unit for feature "${featureSlug}" must be > 0.`
|
|
204
|
+
`Plan "${slug}" configures overage for feature "${featureSlug}", which is not of type 'metered'.`
|
|
204
205
|
);
|
|
205
206
|
}
|
|
206
207
|
}
|
|
207
208
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
function validateFeatureLimits(productType, slug, features, errors) {
|
|
212
|
-
for (const [featureSlug, value] of Object.entries(features)) {
|
|
213
|
-
if (value.value_limit !== void 0 && value.value_limit < 0) {
|
|
214
|
-
errors.push(
|
|
215
|
-
`${productType} "${slug}" \u2192 feature "${featureSlug}" has a negative value_limit (${value.value_limit}).`
|
|
216
|
-
);
|
|
217
|
-
}
|
|
209
|
+
validatePriceAddonIntervals(slug, index, price, config.addons, errors);
|
|
210
|
+
});
|
|
218
211
|
}
|
|
219
212
|
}
|
|
220
213
|
function validateDefaultPlan(config, errors) {
|
|
@@ -232,21 +225,6 @@ function validateDefaultPlan(config, errors) {
|
|
|
232
225
|
);
|
|
233
226
|
}
|
|
234
227
|
}
|
|
235
|
-
function validateDiscounts(config, errors) {
|
|
236
|
-
if (!config.coupons) return;
|
|
237
|
-
for (const coupon of config.coupons) {
|
|
238
|
-
if (coupon.type === "percent" && (coupon.value < 0 || coupon.value > 100)) {
|
|
239
|
-
errors.push(
|
|
240
|
-
`Discount "${coupon.code}" has an invalid percentage value (${coupon.value}). Must be 0\u2013100.`
|
|
241
|
-
);
|
|
242
|
-
}
|
|
243
|
-
if (coupon.type === "amount" && coupon.value < 0) {
|
|
244
|
-
errors.push(
|
|
245
|
-
`Discount "${coupon.code}" has a negative amount value (${coupon.value}).`
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
228
|
function validateConfig(config) {
|
|
251
229
|
const errors = [];
|
|
252
230
|
const knownFeatureSlugs = new Set(Object.keys(config.features));
|
|
@@ -259,8 +237,7 @@ function validateConfig(config) {
|
|
|
259
237
|
knownFeatureSlugs,
|
|
260
238
|
errors
|
|
261
239
|
);
|
|
262
|
-
|
|
263
|
-
validateFeatureLimits("Plan", slug, plan.features, errors);
|
|
240
|
+
validatePlanPricing(slug, plan.prices, config, errors);
|
|
264
241
|
}
|
|
265
242
|
if (config.addons) {
|
|
266
243
|
for (const [slug, addon] of Object.entries(config.addons)) {
|
|
@@ -271,18 +248,168 @@ function validateConfig(config) {
|
|
|
271
248
|
knownFeatureSlugs,
|
|
272
249
|
errors
|
|
273
250
|
);
|
|
274
|
-
validatePricing("Addon", slug, addon.prices, config.features, errors);
|
|
275
|
-
validateFeatureLimits("Addon", slug, addon.features, errors);
|
|
276
251
|
}
|
|
277
252
|
}
|
|
278
|
-
validateDiscounts(config, errors);
|
|
279
253
|
if (errors.length > 0) {
|
|
280
254
|
throw new RevstackValidationError(errors);
|
|
281
255
|
}
|
|
282
256
|
}
|
|
257
|
+
|
|
258
|
+
// src/schema.ts
|
|
259
|
+
import { z } from "zod";
|
|
260
|
+
var FeatureTypeSchema = z.enum(["boolean", "static", "metered"]);
|
|
261
|
+
var UnitTypeSchema = z.enum([
|
|
262
|
+
"count",
|
|
263
|
+
"bytes",
|
|
264
|
+
"seconds",
|
|
265
|
+
"tokens",
|
|
266
|
+
"requests",
|
|
267
|
+
"custom"
|
|
268
|
+
]);
|
|
269
|
+
var ResetPeriodSchema = z.enum([
|
|
270
|
+
"daily",
|
|
271
|
+
"weekly",
|
|
272
|
+
"monthly",
|
|
273
|
+
"yearly",
|
|
274
|
+
"never"
|
|
275
|
+
]);
|
|
276
|
+
var BillingIntervalSchema = z.enum([
|
|
277
|
+
"monthly",
|
|
278
|
+
"quarterly",
|
|
279
|
+
"yearly",
|
|
280
|
+
"one_time"
|
|
281
|
+
]);
|
|
282
|
+
var PlanTypeSchema = z.enum(["paid", "free", "custom"]);
|
|
283
|
+
var PlanStatusSchema = z.enum(["draft", "active", "archived"]);
|
|
284
|
+
var SubscriptionStatusSchema = z.enum([
|
|
285
|
+
"active",
|
|
286
|
+
"trialing",
|
|
287
|
+
"past_due",
|
|
288
|
+
"canceled",
|
|
289
|
+
"paused"
|
|
290
|
+
]);
|
|
291
|
+
var FeatureDefInputSchema = z.object({
|
|
292
|
+
name: z.string(),
|
|
293
|
+
description: z.string().optional(),
|
|
294
|
+
type: FeatureTypeSchema,
|
|
295
|
+
unit_type: UnitTypeSchema
|
|
296
|
+
});
|
|
297
|
+
var PlanFeatureValueSchema = z.object({
|
|
298
|
+
value_limit: z.number().min(0).optional(),
|
|
299
|
+
value_bool: z.boolean().optional(),
|
|
300
|
+
value_text: z.string().optional(),
|
|
301
|
+
is_hard_limit: z.boolean().optional(),
|
|
302
|
+
reset_period: ResetPeriodSchema.optional()
|
|
303
|
+
});
|
|
304
|
+
var AddonFeatureValueSchema = z.object({
|
|
305
|
+
value_limit: z.number().min(0).optional(),
|
|
306
|
+
type: z.enum(["increment", "set"]).optional(),
|
|
307
|
+
has_access: z.boolean().optional(),
|
|
308
|
+
is_hard_limit: z.boolean().optional()
|
|
309
|
+
});
|
|
310
|
+
var OverageConfigurationSchema = z.record(
|
|
311
|
+
z.string(),
|
|
312
|
+
z.object({
|
|
313
|
+
overage_amount: z.number().min(0),
|
|
314
|
+
overage_unit: z.number().min(1)
|
|
315
|
+
})
|
|
316
|
+
);
|
|
317
|
+
var PriceDefSchema = z.object({
|
|
318
|
+
amount: z.number().min(0),
|
|
319
|
+
currency: z.string(),
|
|
320
|
+
billing_interval: BillingIntervalSchema,
|
|
321
|
+
trial_period_days: z.number().min(0).optional(),
|
|
322
|
+
is_active: z.boolean().optional(),
|
|
323
|
+
overage_configuration: OverageConfigurationSchema.optional(),
|
|
324
|
+
available_addons: z.array(z.string()).optional()
|
|
325
|
+
});
|
|
326
|
+
var PlanDefInputSchema = z.object({
|
|
327
|
+
name: z.string(),
|
|
328
|
+
description: z.string().optional(),
|
|
329
|
+
is_default: z.boolean(),
|
|
330
|
+
is_public: z.boolean(),
|
|
331
|
+
type: PlanTypeSchema,
|
|
332
|
+
status: PlanStatusSchema.optional().default("active"),
|
|
333
|
+
prices: z.array(PriceDefSchema).optional(),
|
|
334
|
+
features: z.record(z.string(), PlanFeatureValueSchema)
|
|
335
|
+
});
|
|
336
|
+
var BaseAddonDefInput = z.object({
|
|
337
|
+
name: z.string(),
|
|
338
|
+
description: z.string().optional(),
|
|
339
|
+
amount: z.number().min(0),
|
|
340
|
+
currency: z.string(),
|
|
341
|
+
features: z.record(z.string(), AddonFeatureValueSchema)
|
|
342
|
+
});
|
|
343
|
+
var RecurringAddonSchema = BaseAddonDefInput.extend({
|
|
344
|
+
type: z.literal("recurring"),
|
|
345
|
+
billing_interval: z.enum(["monthly", "quarterly", "yearly"])
|
|
346
|
+
});
|
|
347
|
+
var OneTimeAddonSchema = BaseAddonDefInput.extend({
|
|
348
|
+
type: z.literal("one_time"),
|
|
349
|
+
// omitted/ignored for one_time
|
|
350
|
+
billing_interval: z.any().optional()
|
|
351
|
+
});
|
|
352
|
+
var AddonDefInputSchema = z.discriminatedUnion("type", [
|
|
353
|
+
RecurringAddonSchema,
|
|
354
|
+
OneTimeAddonSchema
|
|
355
|
+
]);
|
|
356
|
+
var DiscountTypeSchema = z.enum(["percent", "amount"]);
|
|
357
|
+
var DiscountDurationSchema = z.enum(["once", "forever", "repeating"]);
|
|
358
|
+
var BaseDiscountDef = z.object({
|
|
359
|
+
code: z.string(),
|
|
360
|
+
name: z.string().optional(),
|
|
361
|
+
applies_to_plans: z.array(z.string()).optional(),
|
|
362
|
+
max_redemptions: z.number().min(1).optional(),
|
|
363
|
+
expires_at: z.string().datetime().optional()
|
|
364
|
+
});
|
|
365
|
+
var DiscountValueSchema = z.discriminatedUnion("type", [
|
|
366
|
+
z.object({ type: z.literal("percent"), value: z.number().min(0).max(100) }),
|
|
367
|
+
z.object({ type: z.literal("amount"), value: z.number().min(0) })
|
|
368
|
+
]);
|
|
369
|
+
var DiscountDurationLogic = z.discriminatedUnion("duration", [
|
|
370
|
+
z.object({
|
|
371
|
+
duration: z.literal("once"),
|
|
372
|
+
duration_in_months: z.undefined().optional()
|
|
373
|
+
}),
|
|
374
|
+
z.object({
|
|
375
|
+
duration: z.literal("forever"),
|
|
376
|
+
duration_in_months: z.undefined().optional()
|
|
377
|
+
}),
|
|
378
|
+
z.object({
|
|
379
|
+
duration: z.literal("repeating"),
|
|
380
|
+
duration_in_months: z.number().min(1)
|
|
381
|
+
})
|
|
382
|
+
]);
|
|
383
|
+
var DiscountDefSchema = BaseDiscountDef.and(DiscountValueSchema).and(
|
|
384
|
+
DiscountDurationLogic
|
|
385
|
+
);
|
|
386
|
+
var RevstackConfigSchema = z.object({
|
|
387
|
+
features: z.record(z.string(), FeatureDefInputSchema),
|
|
388
|
+
plans: z.record(z.string(), PlanDefInputSchema),
|
|
389
|
+
addons: z.record(z.string(), AddonDefInputSchema).optional(),
|
|
390
|
+
coupons: z.array(DiscountDefSchema).optional()
|
|
391
|
+
});
|
|
283
392
|
export {
|
|
393
|
+
AddonDefInputSchema,
|
|
394
|
+
AddonFeatureValueSchema,
|
|
395
|
+
BillingIntervalSchema,
|
|
396
|
+
DiscountDefSchema,
|
|
397
|
+
DiscountDurationSchema,
|
|
398
|
+
DiscountTypeSchema,
|
|
284
399
|
EntitlementEngine,
|
|
400
|
+
FeatureDefInputSchema,
|
|
401
|
+
FeatureTypeSchema,
|
|
402
|
+
OverageConfigurationSchema,
|
|
403
|
+
PlanDefInputSchema,
|
|
404
|
+
PlanFeatureValueSchema,
|
|
405
|
+
PlanStatusSchema,
|
|
406
|
+
PlanTypeSchema,
|
|
407
|
+
PriceDefSchema,
|
|
408
|
+
ResetPeriodSchema,
|
|
409
|
+
RevstackConfigSchema,
|
|
285
410
|
RevstackValidationError,
|
|
411
|
+
SubscriptionStatusSchema,
|
|
412
|
+
UnitTypeSchema,
|
|
286
413
|
defineAddon,
|
|
287
414
|
defineConfig,
|
|
288
415
|
defineDiscount,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@revstackhq/core",
|
|
3
|
-
"version": "0.0.0-dev-
|
|
3
|
+
"version": "0.0.0-dev-20260228062053",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "FSL-1.1-MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -31,6 +31,9 @@
|
|
|
31
31
|
"publishConfig": {
|
|
32
32
|
"access": "public"
|
|
33
33
|
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"zod": "^4.3.6"
|
|
36
|
+
},
|
|
34
37
|
"scripts": {
|
|
35
38
|
"build": "tsup",
|
|
36
39
|
"dev": "tsup --watch",
|