{"version":3,"file":"in-app.mjs","names":[],"sources":["../../../../../../../notifications/src/in-app/in-app.ts"],"sourcesContent":["/**\n * `inApp` — app-facing facade for the in-app/database channel.\n *\n * `inApp.configure({ model | repository })` binds the in-app store AND\n * returns the `database` channel — so the read side and the dispatched\n * write side share ONE repository instance.\n *   • `{ model: Notification }`  — 90% case; default repo built internally.\n *   • `{ repository: myRepo }`   — 10% case; custom column mapping / extras.\n *\n * No zero-arg form: the package ships no concrete model/table (thin eject),\n * so the dev MUST tell us which model to use.\n *\n * Every read + mutation is RECIPIENT-SCOPED by construction — `markAsRead(user,\n * id)` cannot flip a row belonging to a different recipient even if handed a\n * foreign id (the recipient id is forced into the filter → 0 rows match).\n *\n * @example\n *   // src/config/notifications.ts:\n *   inApp.configure({ model: Notification }),\n *\n *   // anywhere:\n *   const unread = await inApp.listUnread(user);\n *   const badge  = await inApp.countUnread(user);\n *   const one    = await inApp.find(user, \"ntf_123\");\n *   await inApp.markAsRead(user, \"ntf_123\");\n *   await inApp.markAsRead(user);            // mark ALL unread\n *   await inApp.dismiss(user, \"ntf_123\");    // delete one\n *   await inApp.dismiss(user);               // clear all for this recipient\n */\nimport type { TypedRepositoryOptionsWithPages } from \"@warlock.js/core\";\nimport { databaseChannel } from \"../channels/database-channel\";\nimport type { Channel } from \"../contracts\";\nimport type { DatabasePayload, Id, Notifiable } from \"../types\";\nimport {\n  BaseNotificationsRepository,\n  type NotificationModelClass,\n  type NotificationsFilter,\n} from \"./base-notifications-repository\";\n\n/**\n * List options for the read methods — the repository's paginated options\n * (`page` / `limit` / `orderBy` / filter keys). `recipientId` is always forced\n * from the recipient argument, so passing it here has no effect.\n */\nexport type NotificationsListOptions = TypedRepositoryOptionsWithPages<NotificationsFilter>;\n\n/**\n * Configure options — discriminated so `model` and `repository` are mutually\n * exclusive at the type level. Future knobs (cache / realtime / naming) slot\n * in here without breaking the signature.\n */\nexport type ConfigureOptions =\n  | { model: NotificationModelClass; repository?: never }\n  | { repository: BaseNotificationsRepository; model?: never };\n\nconst idOf = (recipient: Notifiable | Id): Id =>\n  typeof recipient === \"object\" ? recipient.id : recipient;\n\nclass InApp {\n  private repo?: BaseNotificationsRepository;\n\n  /**\n   * Bind the in-app store AND return the `database` channel. Called once\n   * from `config/notifications.ts`. Subsequent calls REPLACE the binding.\n   */\n  public configure(options: ConfigureOptions): Channel<DatabasePayload> {\n    this.repo =\n      \"repository\" in options && options.repository\n        ? options.repository\n        : new BaseNotificationsRepository(options.model);\n    return databaseChannel(this.repo);\n  }\n\n  private get repository(): BaseNotificationsRepository {\n    if (!this.repo) {\n      throw new Error(\n        \"In-app notifications not configured — add \" +\n          \"`database: inApp.configure({ model: Notification })` to config/notifications.ts\",\n      );\n    }\n    return this.repo;\n  }\n\n  /** General list — scoped to recipient; pass `options` for paging + filters. */\n  public list(recipient: Notifiable | Id, options?: NotificationsListOptions) {\n    return this.repository.list({ ...options, recipientId: idOf(recipient) });\n  }\n\n  /** Unread-only list — common case for badges + dashboards. */\n  public listUnread(recipient: Notifiable | Id, options?: NotificationsListOptions) {\n    return this.repository.list({ ...options, recipientId: idOf(recipient), unread: true });\n  }\n\n  /**\n   * Cached unread count — backs the badge. Auto-invalidated by the repo's\n   * model create/update events (`RepositoryManager.registerEvents`).\n   */\n  public countUnread(recipient: Notifiable | Id) {\n    return this.repository.countCached({ recipientId: idOf(recipient), unread: true });\n  }\n\n  /** Find one notification for a recipient — for a detail view. */\n  public find(recipient: Notifiable | Id, id: Id) {\n    return this.repository.findFor(idOf(recipient), id);\n  }\n\n  /** Mark read — `id` omitted = all unread for this recipient. */\n  public markAsRead(recipient: Notifiable | Id, id?: Id) {\n    return this.repository.markRead(idOf(recipient), id);\n  }\n\n  /** Mark unread — same recipient-scoping. */\n  public markAsUnread(recipient: Notifiable | Id, id?: Id) {\n    return this.repository.markUnread(idOf(recipient), id);\n  }\n\n  /** Delete/dismiss — `id` omitted = clear all for this recipient. */\n  public dismiss(recipient: Notifiable | Id, id?: Id) {\n    return this.repository.deleteFor(idOf(recipient), id);\n  }\n}\n\n/** Single in-app facade — bound at boot via `inApp.configure(...)`. */\nexport const inApp = new InApp();\n"],"mappings":";;;;AAuDA,MAAM,QAAQ,cACZ,OAAO,cAAc,WAAW,UAAU,KAAK;AAEjD,IAAM,QAAN,MAAY;CACV,AAAQ;;;;;CAMR,AAAO,UAAU,SAAqD;EACpE,KAAK,OACH,gBAAgB,WAAW,QAAQ,aAC/B,QAAQ,aACR,IAAI,4BAA4B,QAAQ,KAAK;EACnD,OAAO,gBAAgB,KAAK,IAAI;CAClC;CAEA,IAAY,aAA0C;EACpD,IAAI,CAAC,KAAK,MACR,MAAM,IAAI,MACR,2HAEF;EAEF,OAAO,KAAK;CACd;;CAGA,AAAO,KAAK,WAA4B,SAAoC;EAC1E,OAAO,KAAK,WAAW,KAAK;GAAE,GAAG;GAAS,aAAa,KAAK,SAAS;EAAE,CAAC;CAC1E;;CAGA,AAAO,WAAW,WAA4B,SAAoC;EAChF,OAAO,KAAK,WAAW,KAAK;GAAE,GAAG;GAAS,aAAa,KAAK,SAAS;GAAG,QAAQ;EAAK,CAAC;CACxF;;;;;CAMA,AAAO,YAAY,WAA4B;EAC7C,OAAO,KAAK,WAAW,YAAY;GAAE,aAAa,KAAK,SAAS;GAAG,QAAQ;EAAK,CAAC;CACnF;;CAGA,AAAO,KAAK,WAA4B,IAAQ;EAC9C,OAAO,KAAK,WAAW,QAAQ,KAAK,SAAS,GAAG,EAAE;CACpD;;CAGA,AAAO,WAAW,WAA4B,IAAS;EACrD,OAAO,KAAK,WAAW,SAAS,KAAK,SAAS,GAAG,EAAE;CACrD;;CAGA,AAAO,aAAa,WAA4B,IAAS;EACvD,OAAO,KAAK,WAAW,WAAW,KAAK,SAAS,GAAG,EAAE;CACvD;;CAGA,AAAO,QAAQ,WAA4B,IAAS;EAClD,OAAO,KAAK,WAAW,UAAU,KAAK,SAAS,GAAG,EAAE;CACtD;AACF;;AAGA,MAAa,QAAQ,IAAI,MAAM"}