{"version":3,"file":"column-map.mjs","names":[],"sources":["../../../../../../../notifications/src/in-app/column-map.ts"],"sourcesContent":["/**\n * `columnMap` — how a notification model's logical roles map to the physical\n * columns of ITS table. The ONE source the model getters, the repository\n * (read filter + write mapping), the database channel, and the migration\n * factory all read, so they can never drift apart.\n *\n * Declared as a `static columnMap` on the model and ejected to userland, so\n * the dev owns it:\n *\n *   @RegisterModel()\n *   export class Notification extends DatabaseNotification {\n *     public static table = \"notifications\";\n *     public static columnMap: NotificationColumnMap = {\n *       tenant: \"organization_id\", // omit → single-tenant\n *       readAt: \"read_at\",         // read-state (see below)\n *     };\n *   }\n *\n * ## Read-state is chosen by which keys are PRESENT\n *\n * - `readAt` only → unread = `read_at IS NULL`; marking read stamps it.\n * - `isRead` only → unread = `is_read = false`; no timestamp.\n * - both        → `is_read` is the indexed flag used for filtering, `read_at`\n *                 records WHEN; marking read sets both.\n *\n * Declare neither and you get the `readAt`-only default — so a model can omit\n * `columnMap` entirely and still work.\n */\n\n/**\n * The logical → physical column bindings a notification model may declare.\n * Every key is optional; `resolveColumnMap` fills the defaults. Values are the\n * app's real column names (rename-friendly: `readAt: \"seen_at\"`).\n */\nexport type NotificationColumnMap = {\n  /** Recipient FK column. Default: `\"user_id\"`. */\n  recipient?: string;\n  /** Multi-tenant scope column, written from the recipient. Omit → single-tenant. */\n  tenant?: string;\n  /** Read-timestamp column. Present → read-state records \"when\". */\n  readAt?: string;\n  /** Read-flag column (indexed → fast unread filter). Present → read-state is a boolean. */\n  isRead?: string;\n};\n\n/**\n * A `NotificationColumnMap` with the defaults applied. `recipient` is always\n * set, and at least one of `readAt` / `isRead` is always present (the resolver\n * falls back to `readAt`), so consumers never face a \"no read-state\" map.\n */\nexport type ResolvedNotificationColumnMap = {\n  recipient: string;\n  tenant?: string;\n  readAt?: string;\n  isRead?: string;\n};\n\n/** A model class that may carry the `columnMap` static. */\nexport type NotificationColumnMapHost = {\n  columnMap?: NotificationColumnMap;\n};\n\nconst DEFAULT_RECIPIENT_COLUMN = \"user_id\";\nconst DEFAULT_READ_AT_COLUMN = \"read_at\";\n\n/**\n * Resolve a model's declared `columnMap` into a complete map: default the\n * recipient column, and fall back to a `read_at` timestamp when the model\n * declares no read-state column. The fallback guarantees the result always\n * has a usable read-state representation, so no boot-time validation is needed.\n */\nexport function resolveColumnMap(\n  map: NotificationColumnMap | undefined,\n): ResolvedNotificationColumnMap {\n  const recipient = map?.recipient ?? DEFAULT_RECIPIENT_COLUMN;\n  const tenant = map?.tenant;\n\n  if (!map?.readAt && !map?.isRead) {\n    return { recipient, tenant, readAt: DEFAULT_READ_AT_COLUMN };\n  }\n\n  return {\n    recipient,\n    tenant,\n    readAt: map.readAt,\n    isRead: map.isRead,\n  };\n}\n"],"mappings":";AA8DA,MAAM,2BAA2B;AACjC,MAAM,yBAAyB;;;;;;;AAQ/B,SAAgB,iBACd,KAC+B;CAC/B,MAAM,YAAY,KAAK,aAAa;CACpC,MAAM,SAAS,KAAK;CAEpB,IAAI,CAAC,KAAK,UAAU,CAAC,KAAK,QACxB,OAAO;EAAE;EAAW;EAAQ,QAAQ;CAAuB;CAG7D,OAAO;EACL;EACA;EACA,QAAQ,IAAI;EACZ,QAAQ,IAAI;CACd;AACF"}