Files
2026-07-13 13:32:57 +08:00

182 lines
5.3 KiB
TypeScript

import type { BillingClient } from "@trigger.dev/platform";
import { z } from "zod";
/**
* Billing limit API schemas for the billing platform service.
*
* These mirror the planned @trigger.dev/platform types and are used via
* BillingClient.fetch until the platform package is published with native
* BillingClient methods.
*/
export const BillingLimitStateSchema = z.discriminatedUnion("status", [
z.object({
status: z.literal("ok"),
}),
z.object({
status: z.literal("grace"),
hitAt: z.string().datetime({ offset: true }),
graceEndsAt: z.string().datetime({ offset: true }),
}),
z.object({
status: z.literal("rejected"),
hitAt: z.string().datetime({ offset: true }),
graceEndsAt: z.string().datetime({ offset: true }),
}),
]);
export type BillingLimitState = z.infer<typeof BillingLimitStateSchema>;
export const BillingLimitConfigSchema = z.discriminatedUnion("mode", [
z.object({
mode: z.literal("none"),
}),
z.object({
mode: z.literal("plan"),
}),
z.object({
mode: z.literal("custom"),
amountCents: z.number().int().positive(),
}),
]);
export type BillingLimitConfig = z.infer<typeof BillingLimitConfigSchema>;
export const BillingLimitUnconfiguredSchema = z.object({
isConfigured: z.literal(false),
gracePeriodMs: z.number().int().nonnegative(),
});
const billingLimitConfiguredFields = {
isConfigured: z.literal(true),
cancelInProgressRuns: z.boolean(),
limitState: BillingLimitStateSchema,
effectiveAmountCents: z.number().int().nonnegative().nullable(),
gracePeriodMs: z.number().int().nonnegative(),
};
export const BillingLimitConfiguredNoneSchema = z.object({
...billingLimitConfiguredFields,
mode: z.literal("none"),
});
export const BillingLimitConfiguredPlanSchema = z.object({
...billingLimitConfiguredFields,
mode: z.literal("plan"),
});
export const BillingLimitConfiguredCustomSchema = z.object({
...billingLimitConfiguredFields,
mode: z.literal("custom"),
amountCents: z.number().int().positive(),
});
export const BillingLimitConfiguredSchema = z.discriminatedUnion("mode", [
BillingLimitConfiguredNoneSchema,
BillingLimitConfiguredPlanSchema,
BillingLimitConfiguredCustomSchema,
]);
export const BillingLimitResultSchema = z.union([
BillingLimitUnconfiguredSchema,
BillingLimitConfiguredNoneSchema,
BillingLimitConfiguredPlanSchema,
BillingLimitConfiguredCustomSchema,
]);
export type BillingLimitResult = z.infer<typeof BillingLimitResultSchema>;
export const UpdateBillingLimitRequestSchema = z.discriminatedUnion("mode", [
z.object({
mode: z.literal("none"),
cancelInProgressRuns: z.boolean(),
}),
z.object({
mode: z.literal("plan"),
cancelInProgressRuns: z.boolean(),
}),
z.object({
mode: z.literal("custom"),
amountCents: z.number().int().positive(),
cancelInProgressRuns: z.boolean(),
}),
]);
export type UpdateBillingLimitRequest = z.infer<typeof UpdateBillingLimitRequestSchema>;
export const ResolveBillingLimitRequestSchema = z.discriminatedUnion("action", [
z.object({
action: z.literal("increase"),
newAmountCents: z.number().int().positive(),
resumeMode: z.enum(["queue", "new_only"]),
}),
z.object({
action: z.literal("remove"),
resumeMode: z.enum(["queue", "new_only"]),
}),
]);
export type ResolveBillingLimitRequest = z.infer<typeof ResolveBillingLimitRequestSchema>;
export const BillingLimitActiveOrgSchema = z.object({
orgId: z.string(),
limitState: z.enum(["grace", "rejected"]),
});
export const BillingLimitsActiveResultSchema = z.object({
orgs: z.array(BillingLimitActiveOrgSchema),
});
export type BillingLimitsActiveResult = z.infer<typeof BillingLimitsActiveResultSchema>;
export const BillingLimitPendingResolveOrgSchema = z.object({
organizationId: z.string(),
resumeMode: z.enum(["queue", "new_only"]),
resolvedAt: z.string().datetime({ offset: true }),
});
export const BillingLimitsPendingResolvesResultSchema = z.object({
orgs: z.array(BillingLimitPendingResolveOrgSchema),
});
export type BillingLimitsPendingResolvesResult = z.infer<
typeof BillingLimitsPendingResolvesResultSchema
>;
export const BillingLimitHitWebhookBodySchema = z.object({
hitAt: z.string().datetime({ offset: true }),
cancelInProgressRuns: z.boolean(),
limitState: z.literal("grace"),
});
export type BillingLimitHitWebhookBody = z.infer<typeof BillingLimitHitWebhookBodySchema>;
/** Entitlement response — mirrors ReportUsageResult with billing limit fields until platform ships native types. */
export const EntitlementResultSchema = z.object({
hasAccess: z.boolean(),
balance: z.number().optional(),
usage: z.number().optional(),
overage: z.number().optional(),
plan: z
.object({
type: z.string(),
code: z.string(),
isPaying: z.boolean(),
})
.optional(),
limitState: z.literal("grace").optional(),
reason: z.enum(["free_tier_exceeded", "billing_limit"]).optional(),
});
export type EntitlementResult = z.infer<typeof EntitlementResultSchema>;
export type BillingLimitPageData = BillingLimitResult & {
queuedRunCount: number;
currentSpendCents: number;
};
/** Bridge webapp Zod schemas to BillingClient.fetch (separate Zod type instances). */
export function asPlatformSchema(schema: z.ZodTypeAny) {
return schema as unknown as Parameters<BillingClient["fetch"]>[1];
}