import type { Selectable } from 'kysely'
import type * as Db from '../Db.js'
import type * as db_Schema from '../Schema.js'
/** Columns of the `billing_settings` table, derived from `Schema.BillingSettings`. */
export type Table = db_Schema.BillingSettings
/** A stored billing-settings row. */
export type Record = Selectable
/** Currencies limits may be denominated in; single-member today, widens without schema changes. */
export const currencies = ['usd'] as const satisfies readonly Record['currency'][]
/** Spend-limit windows; single-member today, widens without schema changes. */
export const periods = ['month'] as const satisfies readonly Record['period'][]
/**
* Reads an organization's billing settings.
*
* @param db - The database.
* @param orgId - The organization id (`org_…`).
* @param environment - The environment to read; defaults to `production`.
* @returns The stored record, or `undefined` when the org has none.
*/
export function get(
db: Db.Db,
orgId: string,
environment: Record['environment'] = 'production',
): Promise {
return db.kysely
.selectFrom('billing_settings')
.selectAll()
.where('orgId', '=', orgId)
.where('environment', '=', environment)
.executeTakeFirst()
}
/**
* Upserts an organization's billing settings with PATCH semantics: absent
* fields keep their stored value, `null` clears the limit.
*
* @param db - The database.
* @param input - The settings to apply.
* @returns The stored record.
*/
export async function upsert(db: Db.Db, input: upsert.Input): Promise {
const now = new Date().toISOString()
const environment = input.environment ?? 'production'
return await db.kysely
.insertInto('billing_settings')
.values({
createdAt: now,
currency: input.currency ?? 'usd',
environment,
orgId: input.orgId,
period: input.period ?? 'month',
spendLimit: input.spendLimit ?? null,
txFeeLimit: input.txFeeLimit ?? null,
updatedAt: now,
})
// Targets the `(org_id, environment)` unique index, which exists both
// before and after the org pkey drop release.
.onConflict((oc) =>
oc.columns(['orgId', 'environment']).doUpdateSet({
...(input.currency !== undefined ? { currency: input.currency } : {}),
...(input.period !== undefined ? { period: input.period } : {}),
...(input.spendLimit !== undefined ? { spendLimit: input.spendLimit } : {}),
...(input.txFeeLimit !== undefined ? { txFeeLimit: input.txFeeLimit } : {}),
updatedAt: now,
}),
)
.returningAll()
.executeTakeFirstOrThrow()
}
export declare namespace upsert {
/** Fields accepted when upserting billing settings; absent = unchanged, null = cleared. */
type Input = {
/** Currency both limits are denominated in. */
currency?: Record['currency'] | undefined
/** Environment these settings apply to; defaults to `production`. */
environment?: Record['environment'] | undefined
/** Organization id (`org_…`) the settings belong to. */
orgId: string
/** Spend-limit window. */
period?: Record['period'] | undefined
/** Spend limit per period (decimal string in `currency` units), or null to clear. */
spendLimit?: string | null | undefined
/** Per-transaction fee cap (decimal string in `currency` units), or null to clear. */
txFeeLimit?: string | null | undefined
}
}
/**
* Deletes an organization's billing settings.
*
* @param db - The database.
* @param orgId - The organization id (`org_…`).
* @returns Whether a settings row existed and was deleted.
*/
export async function deleteByOrg(db: Db.Db, orgId: string): Promise {
const result = await db.kysely
.deleteFrom('billing_settings')
.where('orgId', '=', orgId)
.executeTakeFirst()
return result.numDeletedRows > 0n
}