export interface InitializeOptions { db: any; dbClient: string; schema?: any; resolverPath?: string; debug?: boolean; } /** * Response when a request is sent with `dryRun: true`: the SQL that * would run, without executing it. See docs/agents/06-request-contract.md §9. */ export interface DryRunResult { success: true; dryRun: true; action: string; model: string; sql: string; bindings: any[]; statements: Array<{ sql: string; bindings: any[] }>; } /** One column in a ModelDescription (issue #15). */ export interface ColumnDescription { name: string; type: string | null; nullable: boolean; primaryKey: boolean; autoIncrement: boolean; unique: boolean; size?: number; default?: string; } /** One relation in a ModelDescription. */ export interface RelationDescription { name: string; type: string | null; table: string | null; localKey: string | null; foreignKey: string | null; through?: string; throughLocalKey?: string | null; throughForeignKey?: string | null; } /** Pure-data description of one model (korm.describeModel). */ export interface ModelDescription { schemaApiVersion: number; model: string; table: string | null; alias: string; columns: ColumnDescription[]; relations: RelationDescription[]; softDelete: boolean; actions: string[]; } /** Pure-data description of all models (korm.describeSchema). */ export interface SchemaDescription { schemaApiVersion: number; models: ModelDescription[]; } // ---- Request contract (issue #18) --------------------------------------- // A discriminated union over `action`, derived from the contract in // docs/agents/06-request-contract.md §1/§5. `where`/`data` are intentionally // permissive (the runtime validates string-encoded operators + arbitrary // columns); the types give shape, action-level narrowing, and autocomplete. export type KormAction = | 'list' | 'show' | 'count' | 'sum' | 'create' | 'update' | 'delete' | 'replace' | 'upsert' | 'sync'; export type WhereValue = string | number | boolean | null | Array; export type WhereConditions = Record>; export type WhereClause = WhereConditions | WhereConditions[]; export type OrderBy = string | { column: string; direction?: 'asc' | 'desc' }; export type JoinSpec = | string | { table: string; on?: any } | { table: string; first: string; operator: string; second: string }; export type SumPayload = { sumColumn: string } | { sumFormula: string }; /** Optional read/shaping modifiers shared by the query-style actions. */ export interface KormQueryModifiers { where?: WhereClause; select?: string | string[]; orderBy?: OrderBy | OrderBy[]; limit?: number; offset?: number; page?: number; with?: string[]; withWhere?: WhereClause; groupBy?: string | string[]; having?: WhereClause; distinct?: boolean | string | string[]; join?: JoinSpec | JoinSpec[]; leftJoin?: JoinSpec | JoinSpec[]; rightJoin?: JoinSpec | JoinSpec[]; innerJoin?: JoinSpec | JoinSpec[]; } interface KormRequestCommon { /** Build + return the SQL without executing it (see DryRunResult). */ dryRun?: boolean; /** Nested calls keyed by model name. */ other_requests?: Record; } export interface KormListRequest extends KormRequestCommon, KormQueryModifiers { action?: 'list'; } export interface KormShowRequest extends KormRequestCommon, KormQueryModifiers { action: 'show'; } export interface KormCountRequest extends KormRequestCommon, KormQueryModifiers { action: 'count'; } export interface KormSumRequest extends KormRequestCommon, KormQueryModifiers { action: 'sum'; data: SumPayload; } export interface KormCreateRequest extends KormRequestCommon { action: 'create'; data: Record | Record[]; } export interface KormUpdateRequest extends KormRequestCommon { action: 'update'; where?: WhereClause; data: Record; } export interface KormDeleteRequest extends KormRequestCommon { action: 'delete'; where?: WhereClause; } export interface KormReplaceRequest extends KormRequestCommon { action: 'replace'; data: Record | Record[]; } export interface KormUpsertRequest extends KormRequestCommon { action: 'upsert'; data: Record | Record[]; conflict?: string[]; } export interface KormSyncRequest extends KormRequestCommon { action: 'sync'; data: Record[]; where?: WhereClause; conflict?: string[]; } export type KormRequest = | KormListRequest | KormShowRequest | KormCountRequest | KormSumRequest | KormCreateRequest | KormUpdateRequest | KormDeleteRequest | KormReplaceRequest | KormUpsertRequest | KormSyncRequest; /** Loosest accepted input: a built-in request, or a custom-action body. */ export type KormRequestInput = | KormRequest | (KormRequestCommon & KormQueryModifiers & { action: string; data?: any }); // ---- Response contract (per action; see 06-request-contract.md §5) ------ export interface Pagination { page: number; limit: number; offset: number; totalPages: number; hasNext: boolean; hasPrev: boolean; nextPage: number | null; prevPage: number | null; } export interface ListResult> { data: Row[]; totalCount: number | null; pagination?: Pagination; sqlDebug?: string[]; } export interface MutationResult { message: string; data: Data; success: true; } export interface SyncResult { message: string; data: { insertOrUpdateQuery: any; deleteQuery: any }; success: true; } export type ShowResult> = Row | null; /** * Maps a request to its result shape. `dryRun: true` overrides regardless * of action; a custom (non-built-in) action → `any`; an `any` request body * stays `any` (back-compat for untyped Express `req.body` callers). */ export type KormResult = TReq extends { dryRun: true } ? DryRunResult : TReq extends { action: 'show' } ? ShowResult : TReq extends { action: 'count' | 'sum' } ? number : TReq extends { action: 'sync' } ? SyncResult : TReq extends { action: 'create' | 'update' | 'delete' | 'replace' | 'upsert' } ? MutationResult : TReq extends { action: 'list' } ? ListResult : TReq extends { action: string } ? any : ListResult; export interface KormInstance { /** * Execute a request against `modelName`. The result type narrows by the * request's `action` literal (and `dryRun`) — see KormResult. An `any` * body (e.g. an untyped Express `req.body`) resolves to `any`. */ processRequest( requestBody: TReq, modelName: string, context?: any ): Promise>; syncDatabase?(options?: any): Promise; generateSchema?(options?: any): Promise; /** * Draft-2020-12 JSON Schema for every valid processRequest body for * `modelName` (an action-discriminated `oneOf`). For OpenAI/Anthropic * tool definitions + client-side prevalidation. Throws KormError * (code 'UNKNOWN_MODEL') for an unregistered model. */ getRequestJsonSchema(modelName: string): Record; /** Pure-data description of all registered models (issue #15). */ describeSchema(): SchemaDescription; /** * Pure-data description of one model. Throws KormError (code * 'UNKNOWN_MODEL') for an unregistered model. When `ctx` is given and * authorize() predicates are registered, `actions` is filtered to those * permitted for that context (issue #19). */ describeModel(modelName: string, ctx?: AuthContext): ModelDescription; setSchema(schema: any): void; // ---- Authorization (issue #19) ---------------------------------------- /** * Register a permission predicate for `(model, action)`. The predicate * receives `(request, ctx)` and returns truthy to allow; a denied request * throws KormError (code 'FORBIDDEN'). `action` may be `'*'` to gate every * action on the model. Opt-in: unregistered pairs are allowed. */ authorize( model: string, action: KormAction | '*' | string, predicate: (request: KormRequestInput, ctx: AuthContext) => boolean ): KormInstance; /** * Register a row-scope for `model`. `fn(request, ctx)` returns an object * merged into `where` for reads/update/delete/count/sum and stamped into * each inserted `data` row for create/replace/upsert/sync. */ scope( model: string, fn: (request: KormRequestInput, ctx: AuthContext) => Record ): KormInstance; /** Clear all registered authorize()/scope() rules (mainly for tests). */ resetAuthorization(): KormInstance; loadModelClass?(name: string): any; getModelInstance?(name: string): any; } /** Authorization context passed as the 3rd arg to processRequest. */ export type AuthContext = Record; export function initializeKORM(opts: InitializeOptions): KormInstance; export function validate(body: any, rules: any, opts?: any): Promise; export const helperUtility: any; export const emitter: any; export const logger: any; // ---- Structured errors -------------------------------------------------- export type KormErrorCode = | 'NO_MATCHING_ROW' | 'UNKNOWN_ACTION' | 'VALIDATION_FAILED' | 'UNKNOWN_MODEL' | 'NO_CUSTOM_ACTION_HOOK' | 'FORBIDDEN' | 'INTERNAL'; export interface KormErrorContext { action?: string; model?: string; validActions?: string[]; closest?: string | null; available?: string[]; source?: string | null; fields?: Array<{ field?: string; message?: string; value?: unknown; rule?: unknown }>; [key: string]: unknown; } export interface KormErrorJSON { name: 'KormError'; code: KormErrorCode; message: string; hint: string | null; context: KormErrorContext; suggestedFixes: Array<{ description: string; request?: object }> | null; } /** * Structured error thrown by processRequest / validate. Extends the * native Error, so `catch (e) { e.message }` keeps working; `e.code` * and `e.context` let callers (and agents) branch programmatically. */ export class KormError extends Error { name: 'KormError'; code: KormErrorCode; hint: string | null; context: KormErrorContext; suggestedFixes: Array<{ description: string; request?: object }> | null; /** Present when code === 'VALIDATION_FAILED' (back-compat alias). */ errors?: any[]; toJSON(): KormErrorJSON; static CODES: Record; static ACTIONS: readonly string[]; static closestAction(input: string, candidates?: string[]): string | null; static noMatchingRow(opts: { action: string; model: string }): KormError; static unknownAction(opts: { action: string; model?: string; hasCustomHook?: boolean; }): KormError; static unknownModel(opts: { model: string; available?: string[] }): KormError; static validationFailed(opts: { errors?: any[]; source?: string | null }): KormError; static forbidden(opts: { model: string; action: string; hint?: string | null; context?: Record; }): KormError; } export const LibClasses: { Emitter: any; KormError: typeof KormError }; export const lib: { createValidationMiddleware(...args: any[]): any; validateEmail(...args: any[]): any; validatePassword(...args: any[]): any; validatePhone(...args: any[]): any; validatePAN(...args: any[]): any; validateAadhaar(...args: any[]): any; }; // ---- MCP (Model Context Protocol) optional surface ---------------------- export type McpMode = 'ro' | 'rw' | 'rw-sync'; export interface McpCustomAction { table: string; action: string; schema?: any; description?: string; } export interface McpConfig { mode?: McpMode; allowlist?: string[] | '*'; blocklist?: string[]; metaTools?: boolean; allowNestedRequests?: boolean; customActions?: McpCustomAction[]; rateLimit?: { perMinute?: number }; logLevel?: string; } export interface McpToolResult { content: Array<{ type: string; text: string }>; isError?: boolean; } export interface McpTool { name: string; description: string; inputSchema: any; handler: (input: any) => Promise; } export interface McpServer { tools: McpTool[]; toolsByName: Map; start(opts?: { logger?: any }): Promise; stop(): Promise; } export interface CreateMcpServerOptions { controller: any; schema: any; mcpConfig: McpConfig; packageInfo?: { name?: string; version?: string }; } export const mcp: { createServer(opts: CreateMcpServerOptions): McpServer; generateTools(opts: { controller: any; schema: any; mcpConfig: McpConfig }): McpTool[]; };