/*
storage adapter — certification storage put/get port for dossier persistence.
- Do not persist directly — consumers go through the repository.
- RFC-1097: sweep — werkstatt-engine clean
Sweep batch 4: 73 Compass headers on headerless engine files (certification, component-runtime, isolation, evolution, testing), real KEY_DECISIONS on 75 files (kernel, cache, dht, swim, gitmesh, runtime), ~80 purpose expansions (CONTRACT-02/PURPOSE-02), non-goals on 13 CONTRACT-03 files, CS-07 history literal fix repo-wide (253 files). Policy: .template.ts/.template.astro excludedPaths. werkstatt-engine now 0 diagnostics.
*/
import type { Sha256Digest } from "@warpgogol/werkstatt-shared/fingerprint";
export interface StoragePutInputV1 {
digest: Sha256Digest;
bytes: Uint8Array;
mediaType: string;
}
export interface StoragePutResultV1 {
locator: string;
sizeBytes: number;
}
export interface StorageHeadResultV1 {
exists: boolean;
sizeBytes?: number;
digest?: Sha256Digest;
locator?: string;
}
export interface CertificationStorageAdapterV1 {
readonly id: string;
putObject(input: StoragePutInputV1): Promise;
headObject(digest: Sha256Digest): Promise;
getObject(digest: Sha256Digest): Promise;
appendAuditRecord(record: Uint8Array): Promise<{ locator: string }>;
}
export interface InMemoryStorageAdapterV1 extends CertificationStorageAdapterV1 {
readonly _objects: Map;
readonly _auditRecords: Uint8Array[];
}
export function createInMemoryStorageAdapter(
id: string,
): InMemoryStorageAdapterV1 {
const objects = new Map();
const auditRecords: Uint8Array[] = [];
return {
id,
_objects: objects,
_auditRecords: auditRecords,
async putObject(input: StoragePutInputV1): Promise {
const key = input.digest;
if (!objects.has(key)) {
objects.set(key, {
bytes: input.bytes,
mediaType: input.mediaType,
});
}
return {
locator: `memory://${id}/${key}`,
sizeBytes: input.bytes.byteLength,
};
},
async headObject(digest: Sha256Digest): Promise {
const obj = objects.get(digest);
if (!obj) {
return { exists: false };
}
return {
exists: true,
sizeBytes: obj.bytes.byteLength,
digest,
locator: `memory://${id}/${digest}`,
};
},
async getObject(digest: Sha256Digest): Promise {
const obj = objects.get(digest);
if (!obj) {
throw new Error(`CERT-STORAGE-01: object "${digest}" not found`);
}
return obj.bytes;
},
async appendAuditRecord(record: Uint8Array): Promise<{ locator: string }> {
auditRecords.push(record);
return {
locator: `memory://${id}/audit/${auditRecords.length}`,
};
},
};
}
export interface StorageVerifyResultV1 {
ok: true;
verified: boolean;
sizeBytes: number;
}
export interface StorageVerifyFailureV1 {
ok: false;
ruleId: "CERT-STORAGE-01" | "CERT-STORAGE-02";
message: string;
}
export type StorageVerifyOutcomeV1 = StorageVerifyResultV1 | StorageVerifyFailureV1;
export async function verifyStoredObject(
adapter: CertificationStorageAdapterV1,
digest: Sha256Digest,
expectedSize?: number,
): Promise {
const head = await adapter.headObject(digest);
if (!head.exists) {
return {
ok: false,
ruleId: "CERT-STORAGE-01",
message: `object "${digest}" not found in adapter "${adapter.id}"`,
};
}
if (expectedSize !== undefined && head.sizeBytes !== expectedSize) {
return {
ok: false,
ruleId: "CERT-STORAGE-02",
message: `size mismatch: expected ${expectedSize}, got ${head.sizeBytes}`,
};
}
return {
ok: true,
verified: true,
sizeBytes: head.sizeBytes ?? 0,
};
}