Skip to main content
Version: v1.0.0(int)

Documents

Polymorphic file storage. Mime-type-agnostic — stores PDFs, images, spreadsheets, anything. Owns both the documents row and the GCS object backing it.

Tables owned

TablePurpose
org.documentsPolymorphic attachment rows. (entity_type, entity_id) link + file metadata (name, mime_type, size_bytes, gcs_path).

Operations

Writes

  • create(conn, input, bytes)Document. Uploads bytes to GCS, inserts the row, returns the hydrated document. input: { entityType, entityId, name, mimeType }. If the row insert fails, L2 deletes the GCS object before re-raising (atomic within the L2 op). If the L3 transaction rolls back after create returned successfully, the GCS object is orphaned and cleaned by a sweeper — out of scope here. pg's PoolClient exposes no rollback callback, so L2 cannot observe L3's commit/rollback after returning.
  • updateMetadata(conn, documentId, patch)Document. Edits display name. Bytes are immutable — replacement = delete + create.
  • delete(conn, documentId)void. Deletes the row and the GCS object. (GCS delete is best-effort if the row deletion succeeds; orphan GCS objects are cleaned by a sweeper, out of scope here.)

Reads

  • getById(conn, documentId)Document | null. Metadata only.
  • getByIdWithSignedUrl(conn, documentId, ttlSeconds)Document & { signedUrl: string } | null. For download.
  • listByEntity(conn, entityType, entityId)Document[]. Documents attached to one entity.
  • listByEntities(conn, entityType, entityIds)Map<entityId, Document[]>. Batch hydration (e.g., SO list with attachment counts).

Notes

  • Component never resolves entity_id back to its source table. Callers must pass a valid id.
  • entity_type values are enum-controlled.
  • This component is consumed by L3 workflows that generate or accept files (PDF export workflows, user uploads, etc.). The component itself doesn't generate any content.