/** * @bytelyst/field-encrypt — Cosmos DB DEK store * * Production DEK store backed by Azure Cosmos DB. * Container: `_encryption_keys` (partition key: /dekId) * * Each WrappedDek is stored as a document with dekId as both id and partition key. */ import type { Container } from '@azure/cosmos'; import type { DekStore, WrappedDek } from './types.js'; interface CosmosDekDoc { id: string; dekId: string; wrappedKey: string; mekVersion: string; createdAt: string; } export class CosmosDekStore implements DekStore { constructor(private readonly container: Container) {} async get(dekId: string): Promise { try { const { resource } = await this.container.item(dekId, dekId).read(); if (!resource) return null; return { dekId: resource.dekId, wrappedKey: resource.wrappedKey, mekVersion: resource.mekVersion, createdAt: resource.createdAt, }; } catch (err: unknown) { if (isNotFound(err)) return null; throw err; } } async put(dek: WrappedDek): Promise { const doc: CosmosDekDoc = { id: dek.dekId, dekId: dek.dekId, wrappedKey: dek.wrappedKey, mekVersion: dek.mekVersion, createdAt: dek.createdAt, }; await this.container.items.upsert(doc); } async listIds(): Promise { const { resources } = await this.container.items .query<{ dekId: string }>('SELECT c.dekId FROM c') .fetchAll(); return resources.map(r => r.dekId); } async delete(dekId: string): Promise { try { await this.container.item(dekId, dekId).delete(); } catch (err: unknown) { if (isNotFound(err)) return; // Already deleted — idempotent throw err; } } } function isNotFound(err: unknown): boolean { return ( typeof err === 'object' && err !== null && 'code' in err && (err as { code: number }).code === 404 ); }