ai_api / src /lib /db /caseMapping.ts
Yogesh
initial deploy
cd8bd0a
Raw
History Blame Contribute Delete
2.76 kB
/**
* db/caseMapping.ts β€” pure snake_case ↔ camelCase column mapping.
*
* Extracted from db/core.ts (god-file decomposition): the column-name conversion
* helpers that translate raw SQLite rows (snake_case columns, 0/1 booleans, `_json`
* TEXT columns) into the camelCase shapes the domain modules consume. Pure β€” no DB
* handle, no module state β€” so they live as a co-located leaf that every db/ module
* (and core.ts itself) imports. core.ts re-exports all five so existing call sites that
* pull these helpers off the core module keep working unchanged.
*/
type JsonRecord = Record<string, unknown>;
export function toSnakeCase(str: string): string {
return str.replace(/([A-Z])/g, "_$1").toLowerCase();
}
export function toCamelCase(str: string): string {
return str.replace(/_([a-z])/g, (_: string, c: string) => c.toUpperCase());
}
export function objToSnake(obj: unknown): unknown {
if (!obj || typeof obj !== "object") return obj;
const result: JsonRecord = {};
for (const [k, v] of Object.entries(obj as JsonRecord)) {
result[toSnakeCase(k)] = v;
}
return result;
}
export function rowToCamel(row: unknown): JsonRecord | null {
if (!row) return null;
const result: JsonRecord = {};
for (const [k, v] of Object.entries(row as JsonRecord)) {
const camelKey = toCamelCase(k);
if (
camelKey === "isActive" ||
camelKey === "rateLimitProtection" ||
camelKey === "proxyEnabled" ||
camelKey === "perKeyProxyEnabled"
) {
result[camelKey] = v === 1 || v === true;
} else if (camelKey === "providerSpecificData" && typeof v === "string") {
try {
result[camelKey] = JSON.parse(v);
} catch {
result[camelKey] = v;
}
} else if (camelKey.endsWith("Json")) {
// Convention: any column with a `_json` suffix is JSON-encoded TEXT.
// Surface the parsed object under the friendlier name (key minus the
// "Json" suffix) β€” e.g. quotaWindowThresholdsJson β†’ quotaWindowThresholds.
// A NULL/absent column normalizes to `baseKey: null` (not the suffixed
// key) so read and write paths expose a consistent shape.
const baseKey = camelKey.slice(0, -"Json".length);
if (typeof v === "string") {
try {
result[baseKey] = JSON.parse(v);
} catch {
result[baseKey] = null;
}
} else {
result[baseKey] = v == null ? null : v;
}
} else {
result[camelKey] = v;
}
}
return result;
}
export function cleanNulls(obj: unknown): JsonRecord {
const result: JsonRecord = {};
for (const [k, v] of Object.entries((obj as JsonRecord) || {})) {
if (v !== null && v !== undefined) {
result[k] = v;
}
}
return result;
}