kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
feat: improve pricing core types
rodzic
5849691936
commit
2656f75b1d
|
@ -100,12 +100,12 @@ export async function resolveOriginRequest(
|
|||
|
||||
if (requestsLineItem) {
|
||||
assert(
|
||||
requestsLineItem?.slug === 'requests',
|
||||
requestsLineItem.slug === 'requests',
|
||||
403,
|
||||
`Invalid pricing plan "${pricingPlan.slug}" for project "${deployment.project}"`
|
||||
)
|
||||
|
||||
rateLimit = requestsLineItem?.rateLimit
|
||||
rateLimit = requestsLineItem.rateLimit
|
||||
} else {
|
||||
// No `requests` line-item, so we don't report usage for this tool.
|
||||
reportUsage = false
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
import { expectTypeOf, test } from 'vitest'
|
||||
|
||||
import type { AgenticProjectConfigInput } from './agentic-project-config'
|
||||
|
||||
test('AgenticProjectConfig input types', () => {
|
||||
expectTypeOf<{
|
||||
name: 'test'
|
||||
originUrl: 'https://httpbin.org'
|
||||
}>().toExtend<AgenticProjectConfigInput>()
|
||||
|
||||
expectTypeOf<{
|
||||
name: 'test'
|
||||
originUrl: 'https://httpbin.org'
|
||||
pricingPlans: [
|
||||
{
|
||||
name: 'Free'
|
||||
slug: 'free'
|
||||
lineItems: [
|
||||
{
|
||||
slug: 'base'
|
||||
usageType: 'licensed'
|
||||
amount: 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}>().toExtend<AgenticProjectConfigInput>()
|
||||
|
||||
expectTypeOf<{
|
||||
name: 'test'
|
||||
originUrl: 'https://httpbin.org'
|
||||
pricingPlans: [
|
||||
{
|
||||
name: 'Basic Monthly'
|
||||
slug: 'basic-monthly'
|
||||
lineItems: [
|
||||
{
|
||||
slug: 'requests'
|
||||
usageType: 'metered'
|
||||
billingScheme: 'per_unit'
|
||||
unitAmount: 50
|
||||
rateLimit: {
|
||||
// Make sure `interval` can use a string as input
|
||||
interval: '30s'
|
||||
maxPerInterval: 100
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}>().toExtend<AgenticProjectConfigInput>()
|
||||
|
||||
expectTypeOf<{
|
||||
name: 'test'
|
||||
originUrl: 'https://httpbin.org'
|
||||
pricingPlans: [
|
||||
{
|
||||
name: 'Basic Monthly'
|
||||
slug: 'basic-monthly'
|
||||
lineItems: [
|
||||
{
|
||||
slug: 'requests'
|
||||
usageType: 'metered'
|
||||
billingScheme: 'per_unit'
|
||||
unitAmount: 50
|
||||
rateLimit: {
|
||||
// Make sure `interval` can use a number as input
|
||||
interval: 300
|
||||
maxPerInterval: 100
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}>().toExtend<AgenticProjectConfigInput>()
|
||||
|
||||
expectTypeOf<{
|
||||
name: 'test'
|
||||
originUrl: 'https://httpbin.org'
|
||||
// Invalid because `pricingPlans` must be non-empty if defined
|
||||
pricingPlans: []
|
||||
}>().not.toExtend<AgenticProjectConfigInput>()
|
||||
|
||||
expectTypeOf<{
|
||||
name: 'test'
|
||||
originUrl: 'https://httpbin.org'
|
||||
pricingPlans: [
|
||||
{
|
||||
name: 'Basic Monthly'
|
||||
slug: 'basic-monthly'
|
||||
// Invalid because `lineItems` must be non-empty
|
||||
lineItems: []
|
||||
}
|
||||
]
|
||||
}>().not.toExtend<AgenticProjectConfigInput>()
|
||||
})
|
|
@ -9,6 +9,7 @@ import {
|
|||
defaultFreePricingPlan,
|
||||
pricingIntervalListSchema,
|
||||
type PricingPlanList,
|
||||
type PricingPlanListInput,
|
||||
pricingPlanListSchema
|
||||
} from './pricing'
|
||||
import { toolConfigSchema, toolSchema } from './tools'
|
||||
|
@ -159,7 +160,7 @@ To add support for annual pricing plans, for example, you can use: \`['month', '
|
|||
|
||||
export type AgenticProjectConfigInput = Simplify<
|
||||
Omit<z.input<typeof agenticProjectConfigSchema>, 'pricingPlans'> & {
|
||||
pricingPlans?: PricingPlanList
|
||||
pricingPlans?: PricingPlanListInput
|
||||
}
|
||||
>
|
||||
export type AgenticProjectConfigRaw = z.output<
|
||||
|
|
|
@ -0,0 +1,225 @@
|
|||
import { assert, expectTypeOf, test } from 'vitest'
|
||||
|
||||
import type {
|
||||
CustomPricingPlanLineItemSlug,
|
||||
PricingPlanLineItem,
|
||||
PricingPlanList
|
||||
} from './pricing'
|
||||
|
||||
test('PricingPlanLineItem "base" type', () => {
|
||||
expectTypeOf({
|
||||
slug: 'base',
|
||||
usageType: 'licensed',
|
||||
amount: 100
|
||||
} as const).toExtend<PricingPlanLineItem>()
|
||||
|
||||
expectTypeOf<{
|
||||
slug: 'base'
|
||||
usageType: 'licensed'
|
||||
amount: number
|
||||
}>().toExtend<PricingPlanLineItem>()
|
||||
})
|
||||
|
||||
test('PricingPlanLineItem "requests" per-unit type', () => {
|
||||
expectTypeOf({
|
||||
slug: 'requests',
|
||||
usageType: 'metered',
|
||||
billingScheme: 'per_unit',
|
||||
unitAmount: 100
|
||||
} as const).toExtend<PricingPlanLineItem>()
|
||||
|
||||
expectTypeOf<{
|
||||
slug: 'requests'
|
||||
usageType: 'metered'
|
||||
billingScheme: 'per_unit'
|
||||
unitAmount: number
|
||||
}>().toExtend<PricingPlanLineItem>()
|
||||
|
||||
expectTypeOf({
|
||||
slug: 'requests',
|
||||
usageType: 'metered',
|
||||
billingScheme: 'per_unit'
|
||||
// invalid because `unitAmount` is required
|
||||
} as const).not.toExtend<PricingPlanLineItem>()
|
||||
|
||||
expectTypeOf<{
|
||||
slug: 'requests'
|
||||
usageType: 'metered'
|
||||
billingScheme: 'per_unit'
|
||||
unitAmount?: number // invalid because `unitAmount` is required
|
||||
}>().not.toExtend<PricingPlanLineItem>()
|
||||
})
|
||||
|
||||
test('PricingPlanLineItem "requests" tiered type', () => {
|
||||
expectTypeOf<{
|
||||
slug: 'requests'
|
||||
usageType: 'metered'
|
||||
billingScheme: 'tiered'
|
||||
tiersMode: 'volume'
|
||||
tiers: [
|
||||
{
|
||||
amount: 300
|
||||
upTo: 1000
|
||||
},
|
||||
{
|
||||
amount: 200
|
||||
upTo: 2000
|
||||
},
|
||||
{
|
||||
amount: 100
|
||||
upTo: 'inf'
|
||||
}
|
||||
]
|
||||
}>().toExtend<PricingPlanLineItem>()
|
||||
})
|
||||
|
||||
test('PricingPlanLineItem "custom" licensed type', () => {
|
||||
expectTypeOf({
|
||||
slug: 'custom-licensed',
|
||||
usageType: 'licensed',
|
||||
amount: 100
|
||||
} as const).toExtend<PricingPlanLineItem>()
|
||||
|
||||
expectTypeOf<{
|
||||
slug: 'custom-licensed'
|
||||
usageType: 'licensed'
|
||||
amount: number
|
||||
}>().toExtend<PricingPlanLineItem>()
|
||||
})
|
||||
|
||||
test('PricingPlanLineItem "custom" metered per-unit type', () => {
|
||||
expectTypeOf({
|
||||
slug: 'custom-test',
|
||||
usageType: 'metered',
|
||||
billingScheme: 'per_unit',
|
||||
unitAmount: 100
|
||||
} as const).toExtend<PricingPlanLineItem>()
|
||||
|
||||
expectTypeOf<{
|
||||
slug: 'custom-test'
|
||||
usageType: 'metered'
|
||||
billingScheme: 'per_unit'
|
||||
unitAmount: number
|
||||
}>().toExtend<PricingPlanLineItem>()
|
||||
})
|
||||
|
||||
test('PricingPlanLineItem "custom" metered tiered type', () => {
|
||||
expectTypeOf<{
|
||||
slug: 'custom-test'
|
||||
usageType: 'metered'
|
||||
billingScheme: 'tiered'
|
||||
tiersMode: 'volume'
|
||||
tiers: [
|
||||
{
|
||||
amount: 300
|
||||
upTo: 1000
|
||||
},
|
||||
{
|
||||
amount: 200
|
||||
upTo: 2000
|
||||
},
|
||||
{
|
||||
amount: 100
|
||||
upTo: 'inf'
|
||||
}
|
||||
]
|
||||
}>().toExtend<PricingPlanLineItem>()
|
||||
})
|
||||
|
||||
test('PricingPlanList type', () => {
|
||||
// Empty array should be invalid
|
||||
expectTypeOf<[]>().not.toExtend<PricingPlanList>()
|
||||
|
||||
// Empty lineItems should be invalid
|
||||
expectTypeOf<
|
||||
[
|
||||
{
|
||||
name: 'Free'
|
||||
slug: 'free'
|
||||
lineItems: []
|
||||
}
|
||||
]
|
||||
>().not.toExtend<PricingPlanList>()
|
||||
|
||||
expectTypeOf<
|
||||
[
|
||||
{
|
||||
name: 'Free'
|
||||
slug: 'free'
|
||||
lineItems: [
|
||||
{
|
||||
slug: 'base'
|
||||
usageType: 'licensed'
|
||||
amount: 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
>().toExtend<PricingPlanList>()
|
||||
})
|
||||
|
||||
test('PricingPlanLineItem "base" type discrimination', () => {
|
||||
const foo: PricingPlanLineItem = {
|
||||
slug: 'base',
|
||||
usageType: 'licensed',
|
||||
amount: 100
|
||||
}
|
||||
|
||||
expectTypeOf(foo).toExtend<PricingPlanLineItem>()
|
||||
|
||||
// These should fail if `slug` is not differentiating correctly.
|
||||
expectTypeOf(foo).toExtend<{
|
||||
slug: 'base'
|
||||
usageType: 'licensed'
|
||||
amount: number
|
||||
label?: string
|
||||
}>()
|
||||
expectTypeOf<typeof foo>().toExtend<{
|
||||
slug: 'base'
|
||||
}>()
|
||||
expectTypeOf<typeof foo>().toExtend<{
|
||||
usageType: 'licensed'
|
||||
}>()
|
||||
})
|
||||
|
||||
test('PricingPlanLineItem "requests" per-unit type discrimination', () => {
|
||||
const foo: PricingPlanLineItem = {
|
||||
slug: 'requests',
|
||||
usageType: 'metered',
|
||||
billingScheme: 'per_unit',
|
||||
unitAmount: 100
|
||||
}
|
||||
|
||||
expectTypeOf(foo).toExtend<PricingPlanLineItem>()
|
||||
|
||||
// These should fail if `slug` is not differentiating correctly.
|
||||
expectTypeOf<typeof foo>().toExtend<{
|
||||
slug: 'requests'
|
||||
}>()
|
||||
expectTypeOf<typeof foo>().toExtend<{
|
||||
usageType: 'metered'
|
||||
}>()
|
||||
expectTypeOf<typeof foo>().toExtend<{
|
||||
billingScheme: 'per_unit'
|
||||
}>()
|
||||
expectTypeOf<typeof foo>().toExtend<{
|
||||
unitAmount: number
|
||||
}>()
|
||||
})
|
||||
|
||||
test('PricingPlanLineItem "metered" type discrimination', () => {
|
||||
const foo = {
|
||||
usageType: 'metered'
|
||||
} as PricingPlanLineItem
|
||||
|
||||
expectTypeOf(foo).toExtend<PricingPlanLineItem>()
|
||||
assert(foo.usageType === 'metered')
|
||||
|
||||
// These should fail if `usageType` is not differentiating correctly.
|
||||
expectTypeOf<typeof foo>().toExtend<{
|
||||
slug: 'requests' | CustomPricingPlanLineItemSlug
|
||||
}>()
|
||||
expectTypeOf<typeof foo>().toExtend<{
|
||||
billingScheme: 'per_unit' | 'tiered'
|
||||
}>()
|
||||
})
|
|
@ -155,9 +155,6 @@ export const pricingPlanLicensedLineItemSchema =
|
|||
amount: z.number().nonnegative()
|
||||
})
|
||||
)
|
||||
export type PricingPlanLicensedLineItem = z.infer<
|
||||
typeof pricingPlanLicensedLineItemSchema
|
||||
>
|
||||
|
||||
/**
|
||||
* Metered LineItems are used to charge for usage-based services.
|
||||
|
@ -275,81 +272,6 @@ export const pricingPlanMeteredLineItemSchema =
|
|||
.optional()
|
||||
})
|
||||
)
|
||||
export type PricingPlanMeteredLineItem =
|
||||
| {
|
||||
usageType: 'metered'
|
||||
billingScheme: 'per_unit'
|
||||
unitAmount: number
|
||||
label?: string
|
||||
unitLabel?: string
|
||||
rateLimit?: {
|
||||
interval: number
|
||||
maxPerInterval: number
|
||||
}
|
||||
defaultAggregation?: {
|
||||
formula: 'sum' | 'count' | 'last'
|
||||
}
|
||||
transformQuantity?: {
|
||||
divideBy: number
|
||||
round: 'down' | 'up'
|
||||
}
|
||||
}
|
||||
| {
|
||||
usageType: 'metered'
|
||||
billingScheme: 'tiered'
|
||||
tiers: PricingPlanTier[]
|
||||
tiersMode: 'graduated' | 'volume'
|
||||
label?: string
|
||||
unitLabel?: string
|
||||
rateLimit?: {
|
||||
interval: number
|
||||
maxPerInterval: number
|
||||
}
|
||||
defaultAggregation?: {
|
||||
formula: 'sum' | 'count' | 'last'
|
||||
}
|
||||
transformQuantity?: {
|
||||
divideBy: number
|
||||
round: 'down' | 'up'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `base` LineItem is used to charge a fixed amount for a service using
|
||||
* `licensed` usage type.
|
||||
*/
|
||||
export const basePricingPlanLineItemSchema =
|
||||
pricingPlanLicensedLineItemSchema.extend({
|
||||
slug: z.literal('base')
|
||||
})
|
||||
export type BasePricingPlanLineItem = z.infer<
|
||||
typeof basePricingPlanLineItemSchema
|
||||
>
|
||||
|
||||
/**
|
||||
* The `requests` LineItem is used to charge for usage-based services using the
|
||||
* `metered` usage type.
|
||||
*
|
||||
* It corresponds to the total number of API calls made by a customer during a
|
||||
* given billing interval.
|
||||
*/
|
||||
export const requestsPricingPlanLineItemSchema =
|
||||
pricingPlanMeteredLineItemSchema.extend({
|
||||
slug: z.literal('requests'),
|
||||
|
||||
/**
|
||||
* Optional label for the line-item which will be displayed on customer
|
||||
* bills.
|
||||
*
|
||||
* If unset, the line-item's `slug` will be used as the unit label.
|
||||
*/
|
||||
unitLabel: z.string().default('API calls').optional()
|
||||
})
|
||||
export type RequestsPricingPlanLineItem = Simplify<
|
||||
PricingPlanMeteredLineItem & {
|
||||
slug: 'requests'
|
||||
}
|
||||
>
|
||||
|
||||
/**
|
||||
* PricingPlanLineItems represent a single line-item in a Stripe Subscription.
|
||||
|
@ -404,37 +326,146 @@ export const pricingPlanLineItemSchema = z
|
|||
.openapi('PricingPlanLineItem')
|
||||
// export type PricingPlanLineItem = z.infer<typeof pricingPlanLineItemSchema>
|
||||
|
||||
// This is a more complex discriminated union based on: `slug`, `usageType`,
|
||||
// and `billingScheme`.
|
||||
// TODO: clean up this type
|
||||
// TODO: add `Input` version to support `string` rateLimit.interval
|
||||
export type PricingPlanLineItem =
|
||||
| BasePricingPlanLineItem
|
||||
| RequestsPricingPlanLineItem
|
||||
| ({
|
||||
slug: CustomPricingPlanLineItemSlug
|
||||
usageType: 'licensed'
|
||||
} & Omit<PricingPlanLicensedLineItem, 'slug' | 'usageType'>)
|
||||
| ({
|
||||
slug: CustomPricingPlanLineItemSlug
|
||||
usageType: 'metered'
|
||||
billingScheme: 'per_unit'
|
||||
} & Omit<
|
||||
PricingPlanMeteredLineItem & {
|
||||
// These are more complex discriminated unions based on: `slug`, `usageType`,
|
||||
// and `billingScheme`. That's why we're not using zod's inference directly
|
||||
// for these types. See `./pricing.test.ts` for examples.
|
||||
export type PricingPlanLineItemInput =
|
||||
// "base" licensed line-item
|
||||
| Simplify<
|
||||
{
|
||||
slug: 'base'
|
||||
} & z.input<typeof pricingPlanLicensedLineItemSchema>
|
||||
>
|
||||
// "custom" licensed line-item
|
||||
| Simplify<
|
||||
{
|
||||
slug: CustomPricingPlanLineItemSlug
|
||||
usageType: 'licensed'
|
||||
} & z.input<typeof pricingPlanLicensedLineItemSchema>
|
||||
>
|
||||
// "requests" metered per-unit line-item
|
||||
| Simplify<
|
||||
{
|
||||
slug: 'requests'
|
||||
usageType: 'metered'
|
||||
billingScheme: 'per_unit'
|
||||
},
|
||||
'slug' | 'usageType' | 'billingScheme' | 'tiers' | 'tiersMode'
|
||||
>)
|
||||
| ({
|
||||
slug: CustomPricingPlanLineItemSlug
|
||||
usageType: 'metered'
|
||||
billingScheme: 'tiered'
|
||||
} & Omit<
|
||||
PricingPlanMeteredLineItem & {
|
||||
unitAmount: number
|
||||
} & Omit<
|
||||
z.input<typeof pricingPlanMeteredLineItemSchema> & {
|
||||
billingScheme: 'per_unit'
|
||||
},
|
||||
'tiers' | 'tiersMode'
|
||||
>
|
||||
>
|
||||
// "requests" metered tiered line-item
|
||||
| Simplify<
|
||||
{
|
||||
slug: 'requests'
|
||||
usageType: 'metered'
|
||||
billingScheme: 'tiered'
|
||||
},
|
||||
'slug' | 'usageType' | 'billingScheme' | 'unitAmount'
|
||||
>)
|
||||
} & Omit<
|
||||
z.input<typeof pricingPlanMeteredLineItemSchema> & {
|
||||
billingScheme: 'tiered'
|
||||
},
|
||||
'unitAmount' | 'transformQuantity'
|
||||
>
|
||||
>
|
||||
// "custom" metered per-unit line-item
|
||||
| Simplify<
|
||||
{
|
||||
slug: CustomPricingPlanLineItemSlug
|
||||
usageType: 'metered'
|
||||
billingScheme: 'per_unit'
|
||||
unitAmount: number
|
||||
} & Omit<
|
||||
z.input<typeof pricingPlanMeteredLineItemSchema> & {
|
||||
billingScheme: 'per_unit'
|
||||
},
|
||||
'tiers' | 'tiersMode'
|
||||
>
|
||||
>
|
||||
// "custom" metered tiered line-item
|
||||
| Simplify<
|
||||
{
|
||||
slug: CustomPricingPlanLineItemSlug
|
||||
usageType: 'metered'
|
||||
billingScheme: 'tiered'
|
||||
} & Omit<
|
||||
z.input<typeof pricingPlanMeteredLineItemSchema> & {
|
||||
billingScheme: 'tiered'
|
||||
},
|
||||
'unitAmount' | 'transformQuantity'
|
||||
>
|
||||
>
|
||||
|
||||
export type PricingPlanLineItem =
|
||||
// "base" licensed line-item
|
||||
| Simplify<
|
||||
{
|
||||
slug: 'base'
|
||||
} & z.infer<typeof pricingPlanLicensedLineItemSchema>
|
||||
>
|
||||
// "custom" licensed line-item
|
||||
| Simplify<
|
||||
{
|
||||
slug: CustomPricingPlanLineItemSlug
|
||||
usageType: 'licensed'
|
||||
} & z.infer<typeof pricingPlanLicensedLineItemSchema>
|
||||
>
|
||||
// "requests" metered per-unit line-item
|
||||
| Simplify<
|
||||
{
|
||||
slug: 'requests'
|
||||
usageType: 'metered'
|
||||
billingScheme: 'per_unit'
|
||||
unitAmount: number
|
||||
} & Omit<
|
||||
z.infer<typeof pricingPlanMeteredLineItemSchema> & {
|
||||
billingScheme: 'per_unit'
|
||||
},
|
||||
'tiers' | 'tiersMode'
|
||||
>
|
||||
>
|
||||
// "requests" metered tiered line-item
|
||||
| Simplify<
|
||||
{
|
||||
slug: 'requests'
|
||||
usageType: 'metered'
|
||||
billingScheme: 'tiered'
|
||||
} & Omit<
|
||||
z.infer<typeof pricingPlanMeteredLineItemSchema> & {
|
||||
billingScheme: 'tiered'
|
||||
},
|
||||
'unitAmount' | 'transformQuantity'
|
||||
>
|
||||
>
|
||||
// "custom" metered per-unit line-item
|
||||
| Simplify<
|
||||
{
|
||||
slug: CustomPricingPlanLineItemSlug
|
||||
usageType: 'metered'
|
||||
billingScheme: 'per_unit'
|
||||
unitAmount: number
|
||||
} & Omit<
|
||||
z.infer<typeof pricingPlanMeteredLineItemSchema> & {
|
||||
billingScheme: 'per_unit'
|
||||
},
|
||||
'tiers' | 'tiersMode'
|
||||
>
|
||||
>
|
||||
// "custom" metered tiered line-item
|
||||
| Simplify<
|
||||
{
|
||||
slug: CustomPricingPlanLineItemSlug
|
||||
usageType: 'metered'
|
||||
billingScheme: 'tiered'
|
||||
} & Omit<
|
||||
z.infer<typeof pricingPlanMeteredLineItemSchema> & {
|
||||
billingScheme: 'tiered'
|
||||
},
|
||||
'unitAmount' | 'transformQuantity'
|
||||
>
|
||||
>
|
||||
|
||||
/**
|
||||
* Represents the config for a single Stripe subscription plan with one or more
|
||||
|
@ -492,9 +523,16 @@ export const pricingPlanSchema = z
|
|||
)
|
||||
.openapi('PricingPlan')
|
||||
// export type PricingPlan = z.infer<typeof pricingPlanSchema>
|
||||
|
||||
export type PricingPlanInput = Simplify<
|
||||
Omit<z.input<typeof pricingPlanSchema>, 'lineItems'> & {
|
||||
lineItems: [PricingPlanLineItemInput, ...PricingPlanLineItemInput[]]
|
||||
}
|
||||
>
|
||||
|
||||
export type PricingPlan = Simplify<
|
||||
Omit<z.infer<typeof pricingPlanSchema>, 'lineItems'> & {
|
||||
lineItems: PricingPlanLineItem[]
|
||||
lineItems: [PricingPlanLineItem, ...PricingPlanLineItem[]]
|
||||
}
|
||||
>
|
||||
|
||||
|
@ -519,7 +557,8 @@ export const pricingPlanListSchema = z
|
|||
message: 'Must contain at least one PricingPlan'
|
||||
})
|
||||
.describe('List of PricingPlans')
|
||||
export type PricingPlanList = PricingPlan[]
|
||||
export type PricingPlanListInput = [PricingPlanInput, ...PricingPlanInput[]]
|
||||
export type PricingPlanList = [PricingPlan, ...PricingPlan[]]
|
||||
|
||||
/**
|
||||
* Map from internal PricingPlanLineItem **slug** to Stripe Subscription Item id
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import { expect, test } from 'vitest'
|
||||
import { expect, expectTypeOf, test } from 'vitest'
|
||||
|
||||
import { rateLimitSchema } from './rate-limit'
|
||||
import {
|
||||
type RateLimit,
|
||||
type RateLimitInput,
|
||||
rateLimitSchema
|
||||
} from './rate-limit'
|
||||
|
||||
test('rateLimitSchema valid', () => {
|
||||
expect(
|
||||
|
@ -54,3 +58,47 @@ test('rateLimitSchema invalid', () => {
|
|||
})
|
||||
).toThrowErrorMatchingSnapshot()
|
||||
})
|
||||
|
||||
test('RateLimit types', () => {
|
||||
expectTypeOf({
|
||||
interval: 10,
|
||||
maxPerInterval: 100
|
||||
} as const).toExtend<RateLimit>()
|
||||
|
||||
expectTypeOf<{
|
||||
interval: 10
|
||||
maxPerInterval: 100
|
||||
}>().toExtend<RateLimit>()
|
||||
|
||||
expectTypeOf({
|
||||
interval: '10s',
|
||||
maxPerInterval: 100
|
||||
} as const).not.toExtend<RateLimit>()
|
||||
|
||||
expectTypeOf<{
|
||||
interval: '10s'
|
||||
maxPerInterval: 100
|
||||
}>().not.toExtend<RateLimit>()
|
||||
})
|
||||
|
||||
test('RateLimitInput types', () => {
|
||||
expectTypeOf({
|
||||
interval: 10,
|
||||
maxPerInterval: 100
|
||||
} as const).toExtend<RateLimitInput>()
|
||||
|
||||
expectTypeOf<{
|
||||
interval: 10
|
||||
maxPerInterval: 100
|
||||
}>().toExtend<RateLimitInput>()
|
||||
|
||||
expectTypeOf({
|
||||
interval: '3h',
|
||||
maxPerInterval: 100
|
||||
} as const).toExtend<RateLimitInput>()
|
||||
|
||||
expectTypeOf<{
|
||||
interval: '3h'
|
||||
maxPerInterval: 100
|
||||
}>().toExtend<RateLimitInput>()
|
||||
})
|
||||
|
|
Ładowanie…
Reference in New Issue