feat: harden CRUD resource contract checks (AEG-V16-020)

Reject incomplete permission, version, concurrency, idempotency, and sensitive-column contracts. Preserve runtime regression evidence; status remains IN_PROGRESS pending predecessor acceptance.
This commit is contained in:
2026-08-08 13:13:32 +09:00
parent 30c941cc5b
commit a84e5c1273
5 changed files with 58 additions and 4 deletions
@@ -17,6 +17,13 @@ export interface CrudResourceDefinition<TQuery, TRow, TForm> {
}
export function assertCrudResourceDefinition(definition: CrudResourceDefinition<unknown, unknown, unknown>): 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) if (!fields.has(sensitive)) throw new Error(`Sensitive field '${sensitive}' has no grid column contract`)
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`)
}
}