export type JsonRecord = Record<string, unknown>
export type PlanetscaleAuthInput = {
/**
* Short account alias. `work` → secret `planetscaleApiToken-work`.
* `default` / `planetscale` / omitted → `planetscaleApiToken`.
*/
account?: string
/** User secret name. Value must be `SERVICE_TOKEN_ID:SERVICE_TOKEN`. */
secretName?: string
}
export type PlanetscalePageInfo = {
currentPage: number | null
nextPage: number | null
prevPage: number | null
}
export type PlanetscaleOrganizationSummary = {
id: string
name: string | null
billingEmail: string | null
}
export type PlanetscaleDatabaseSummary = {
id: string
name: string | null
kind: string | null
region: string | null
htmlUrl: string | null
}
export type PlanetscaleBranchSummary = {
id: string
name: string | null
production: boolean | null
ready: boolean | null
htmlUrl: string | null
parentBranch: string | null
}
export type PlanetscaleQuerySummary = {
id: string
fingerprint: string | null
normalizedSql: string | null
statementType: string | null
keyspace: string | null
tables: Array<string>
queryCount: number | null
p99Latency: number | null
timePerQuery: number | null
sumRowsRead: number | null
errorCount: number | null
lastRunAt: string | null
}
export type PlanetscaleCorrelation = {
id: string
fingerprint: string | null
normalizedSql: string | null
keyspace: string | null
r: number | null
}
export type PlanetscaleAnomalySummary = {
id: string
active: boolean | null
periodStart: string | null
periodEnd: string | null
minutesInViolation: number | null
duration: number | null
correlations: Array<PlanetscaleCorrelation>
}
export type LoopTarget = {
organization: string
database: string
branch: string
githubOwner?: string
githubRepo?: string
repositoryUrl?: string
labels?: Array<string>
}
export type LoopConfig = Partial<LoopTarget> & {
cursorRef?: string
account?: string
githubIntegrationName?: string
githubSecretName?: string
loopEnabled?: boolean
lastProcessedAnomalyId?: string
}
export type PlanetscaleWebhookPayload = {
timestamp?: number
event?: string
organization?: string
database?: string
resource?: {
id?: string
name?: string
html_url?: string
parent_branch?: string
}
}
export type LoopDraft = {
title: string
issueBody: string
implementerPrompt: string
reviewerPrompt: string
humanPing: string
insightsUrl: string
anomaly: PlanetscaleAnomalySummary | null
topQueries: Array<PlanetscaleQuerySummary>
target: LoopTarget
}
export type DryRunResult<T> = {
dryRun: true
wouldCall: T
}
export function requireRecord(value: unknown, label: string): JsonRecord {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
throw new Error(`${label} must be an object.`)
}
return value as JsonRecord
}
export function requireString(
value: unknown,
label: string,
options: { allowEmpty?: boolean } = {},
): string {
if (typeof value !== 'string' || (!options.allowEmpty && value.trim().length === 0)) {
throw new Error(
`${label} must be ${options.allowEmpty ? 'a string' : 'a non-empty string'}.`,
)
}
return options.allowEmpty ? value : value.trim()
}
export function optionalString(value: unknown, label: string): string | undefined {
if (value === undefined || value === null) return undefined
return requireString(value, label)
}
export function optionalBoolean(value: unknown, label: string): boolean | undefined {
if (value === undefined) return undefined
if (typeof value !== 'boolean') throw new Error(`${label} must be a boolean.`)
return value
}
export function optionalStringArray(value: unknown, label: string): Array<string> | undefined {
if (value === undefined || value === null) return undefined
if (!Array.isArray(value)) throw new Error(`${label} must be an array of strings.`)
return value.map((item, index) => requireString(item, `${label}[${index}]`))
}
export function clampInt(
value: unknown,
min: number,
max: number,
fallback: number,
label = 'perPage',
): number {
if (value === undefined || value === null) return fallback
if (typeof value !== 'number' || !Number.isInteger(value)) {
throw new Error(`${label} must be an integer.`)
}
if (value < min || value > max) {
throw new Error(`${label} must be between ${min} and ${max}.`)
}
return value
}
export function compactRecord<T extends JsonRecord>(value: T): T {
const output: JsonRecord = {}
for (const [key, item] of Object.entries(value)) {
if (item === undefined) continue
output[key] = item
}
return output as T
}
export function stringField(value: unknown): string | null {
return typeof value === 'string' && value.length > 0 ? value : null
}
export function booleanField(value: unknown): boolean | null {
return typeof value === 'boolean' ? value : null
}
export function numberField(value: unknown): number | null {
return typeof value === 'number' && Number.isFinite(value) ? value : null
}
/**
* Shared PlanetScale types for this package.
* @example
* import types from 'kody:@kody/planetscale/types'
* const catalog = await types()
*/
export default async function typesOverview() {
return {
auth: ['PlanetscaleAuthInput'],
models: [
'PlanetscaleAnomalySummary',
'PlanetscaleQuerySummary',
'LoopDraft',
'LoopTarget',
'LoopConfig',
'PlanetscaleWebhookPayload',
],
}
}