import { packageStorage } from 'kody:runtime'
import { asLoopConfig } from './loop-helpers.ts'
import type { LoopConfig } from './types.ts'
const CONFIG_KEY = 'loop'
async function withStore<T>(fallback: T, work: (store: ReturnType<typeof packageStorage>) => Promise<T>): Promise<T> {
try {
return await work(packageStorage())
} catch {
// packageStorage is only granted in this package's own runtime.
return fallback
}
}
async function ensureConfigTable(store: ReturnType<typeof packageStorage>) {
await store.sql(
'CREATE TABLE IF NOT EXISTS config (key TEXT PRIMARY KEY, json TEXT NOT NULL, updated_at TEXT NOT NULL)',
[],
)
}
export async function readLoopConfig(): Promise<LoopConfig> {
return await withStore({}, async (store) => {
await ensureConfigTable(store)
const result = await store.sql('SELECT json FROM config WHERE key = ? LIMIT 1', [CONFIG_KEY])
const row = Array.isArray(result?.rows) ? result.rows[0] : null
if (!row?.json) return {}
try {
return asLoopConfig(JSON.parse(String(row.json)))
} catch {
return {}
}
})
}
export async function writeLoopConfig(next: LoopConfig): Promise<LoopConfig> {
let persisted = false
const saved = await withStore(next, async (store) => {
await ensureConfigTable(store)
await store.sql('INSERT OR REPLACE INTO config (key, json, updated_at) VALUES (?, ?, ?)', [
CONFIG_KEY,
JSON.stringify(next),
new Date().toISOString(),
])
persisted = true
return next
})
if (!persisted) {
throw new Error(
'packageStorage is only available when this package is running as itself (a job, webhook, or an export in your fork). Import configure-loop from the fork to save targets, or pass organization/database/repo on each run-loop call.',
)
}
return saved
}