// The plan catalog the Autumn emulator advertises, derived from the deploy-time// source of truth (repo-root `autumn.config.ts`). In production Autumn learns// these plans via `atmn` sync; the emulator has no such sync, so the e2e boot// seeds them. Keeping this derived from autumn.config.ts means plans, prices,// and the Team free trial can never drift from what the app actually ships.import { free, team, enterprise } from "../../autumn.config";type AtmnPlan = { id: string; name: string; addOn?: boolean; autoEnable?: boolean; price?: { amount: number; interval: string }; freeTrial?: { durationLength: number; durationType: string; cardRequired?: boolean }; items?: ReadonlyArray<{ featureId: string; included?: number; unlimited?: boolean }>;};export interface AutumnSeedPlan { id: string; name: string; add_on: boolean; auto_enable: boolean; price: { amount: number; interval: string } | null; free_trial: { duration_length: number; duration_type: string; card_required: boolean } | null; items: Array<{ feature_id: string; included?: number; unlimited?: boolean }>;}const toSeed = (plan: AtmnPlan): AutumnSeedPlan => ({ id: plan.id, name: plan.name, add_on: plan.addOn ?? false, auto_enable: plan.autoEnable ?? false, price: plan.price ? { amount: plan.price.amount, interval: plan.price.interval } : null, free_trial: plan.freeTrial ? { duration_length: plan.freeTrial.durationLength, duration_type: plan.freeTrial.durationType, card_required: plan.freeTrial.cardRequired ?? false, } : null, items: (plan.items ?? []).map((it) => ({ feature_id: it.featureId, included: it.included, unlimited: it.unlimited, })),});/** Free, Team (card-required 14-day trial), and Enterprise, in display order. */export const AUTUMN_PLAN_SEED: AutumnSeedPlan[] = [free, team, enterprise].map((p) => toSeed(p as AtmnPlan),);/** * The "executions" allotment a fresh org has on the auto-enabled default plan * (Free): the exact number of executions it takes to exhaust the quota. Read * from the same seed the emulator is booted with, so a plan change in * autumn.config.ts flows straight into the balance-gate scenarios rather than * drifting against a hardcoded magic number. */export const DEFAULT_PLAN_EXECUTIONS_INCLUDED: number = (() => { const defaultPlan = AUTUMN_PLAN_SEED.find((plan) => plan.auto_enable); const executions = defaultPlan?.items.find((item) => item.feature_id === "executions"); if (executions?.included === undefined) { throw new Error( "autumn-plans: the auto-enabled default plan has no included 'executions' amount to exhaust", ); } return executions.included;})();
// The plan catalog the Autumn emulator advertises, derived from the deploy-time
// source of truth (repo-root `autumn.config.ts`). In production Autumn learns
// these plans via `atmn` sync; the emulator has no such sync, so the e2e boot
// seeds them. Keeping this derived from autumn.config.ts means plans, prices,
// and the Team free trial can never drift from what the app actually ships.
import { free, team, enterprise } from "../../autumn.config";
type AtmnPlan = {
id: string;
name: string;
addOn?: boolean;
autoEnable?: boolean;
price?: { amount: number; interval: string };
freeTrial?: { durationLength: number; durationType: string; cardRequired?: boolean };
items?: ReadonlyArray<{ featureId: string; included?: number; unlimited?: boolean }>;
};
export interface AutumnSeedPlan {
id: string;
name: string;
add_on: boolean;
auto_enable: boolean;
price: { amount: number; interval: string } | null;
free_trial: { duration_length: number; duration_type: string; card_required: boolean } | null;
items: Array<{ feature_id: string; included?: number; unlimited?: boolean }>;
}
const toSeed = (plan: AtmnPlan): AutumnSeedPlan => ({
id: plan.id,
name: plan.name,
add_on: plan.addOn ?? false,
auto_enable: plan.autoEnable ?? false,
price: plan.price ? { amount: plan.price.amount, interval: plan.price.interval } : null,
free_trial: plan.freeTrial
? {
duration_length: plan.freeTrial.durationLength,
duration_type: plan.freeTrial.durationType,
card_required: plan.freeTrial.cardRequired ?? false,
}
: null,
items: (plan.items ?? []).map((it) => ({
feature_id: it.featureId,
included: it.included,
unlimited: it.unlimited,
})),
});
/** Free, Team (card-required 14-day trial), and Enterprise, in display order. */
export const AUTUMN_PLAN_SEED: AutumnSeedPlan[] = [free, team, enterprise].map((p) =>
toSeed(p as AtmnPlan),
);
/**
* The "executions" allotment a fresh org has on the auto-enabled default plan
* (Free): the exact number of executions it takes to exhaust the quota. Read
* from the same seed the emulator is booted with, so a plan change in
* autumn.config.ts flows straight into the balance-gate scenarios rather than
* drifting against a hardcoded magic number.
*/
export const DEFAULT_PLAN_EXECUTIONS_INCLUDED: number = (() => {
const defaultPlan = AUTUMN_PLAN_SEED.find((plan) => plan.auto_enable);
const executions = defaultPlan?.items.find((item) => item.feature_id === "executions");
if (executions?.included === undefined) {
throw new Error(
"autumn-plans: the auto-enabled default plan has no included 'executions' amount to exhaust",
);
}
return executions.included;
})();