import type { ZodType } from 'zod' import type { UiGridColumn } from '../ui/adapter/contracts' export type CrudConcurrencyMode = 'NONE' | 'ETAG_IF_MATCH' export type CrudIdempotencyMode = 'NONE' | 'IDEMPOTENCY_KEY' export type CrudRowField = Extract export interface CrudResourceDefinition { resourceId: string querySchemaVersion: string responseSchemaVersion: string querySchema: ZodType rowSchema: ZodType formSchema: ZodType columns: readonly UiGridColumn[] sensitiveFields: readonly CrudRowField[] permissionPolicy: string concurrencyMode: CrudConcurrencyMode idempotencyMode: CrudIdempotencyMode } export function assertCrudResourceDefinition(definition: CrudResourceDefinition): void { if (!definition.resourceId.trim()) throw new Error('resourceId is required') if (!definition.querySchemaVersion.trim() || !definition.responseSchemaVersion.trim()) throw new Error('schema versions are required') if (!definition.permissionPolicy.trim()) throw new Error('permissionPolicy is required') if (definition.concurrencyMode !== 'NONE' && definition.concurrencyMode !== 'ETAG_IF_MATCH') throw new Error('invalid concurrencyMode') if (definition.idempotencyMode !== 'NONE' && definition.idempotencyMode !== 'IDEMPOTENCY_KEY') throw new Error('invalid idempotencyMode') const fields = new Set(definition.columns.map(x => x.field)) for (const sensitive of definition.sensitiveFields) { const column = definition.columns.find(x => x.field === sensitive) if (!fields.has(sensitive) || !column?.sensitive) throw new Error(`Sensitive field '${sensitive}' has no sensitive grid column contract`) } }