import type { CollectionDomainError, DomainCollection, DomainDescribeInput, DomainObjectResources, } from "@/worker/objects/common/collection-types"; export interface CollectionDescribeSpec< TDb, TCollection extends DomainCollection, TObject, TResource extends { href: string; syncToken?: string }, TError extends CollectionDomainError, > { collectionKind: string; ensureCollection(db: TDb, subjectId: string, nowMs: number, collectionName: string): TCollection | null; homeResource(db: TDb): TResource; listCollections(db: TDb, subjectId: string, nowMs: number): TCollection[]; collectionResource(db: TDb, collection: TCollection): TResource; objectByName(db: TDb, collectionId: string, objectName: string): TObject | undefined; objectResource(db: TDb, collection: TCollection, object: TObject): TResource; objectResources(db: TDb, collection: TCollection, maxResults: number): DomainObjectResources; syncToken(seq: number): string; collectionNotFound(): TError; objectNotFound(): TError; } export function describeCollectionDomain< TDb, TCollection extends DomainCollection, TObject, TResource extends { href: string; syncToken?: string }, TError extends CollectionDomainError, >( db: TDb, input: DomainDescribeInput, spec: CollectionDescribeSpec, ): { ok: true; resources: TResource[]; syncToken: string } | TError { if (input.kind === "home") { const collections = spec.listCollections(db, input.subjectId, input.nowMs); return { ok: true, resources: [spec.homeResource(db), ...collections.map((collection) => spec.collectionResource(db, collection))], syncToken: spec.syncToken(0), }; } const collection = spec.ensureCollection(db, input.subjectId, input.nowMs, input.collectionName ?? ""); if (!collection) return spec.collectionNotFound(); if (input.kind === spec.collectionKind) { const resources = [spec.collectionResource(db, collection)]; if (input.depth === "1") resources.push(...spec.objectResources(db, collection, input.maxResults).resources); return { ok: true, resources, syncToken: resources[0]!.syncToken ?? spec.syncToken(0) }; } const object = spec.objectByName(db, collection.id, input.objectName ?? ""); if (!object) return spec.objectNotFound(); return { ok: true, resources: [spec.objectResource(db, collection, object)], syncToken: spec.syncToken(0) }; }