{"_id":"@bodhveda/js","_rev":"5-d6542841fc9c5029d9cabca3532d24f5","name":"@bodhveda/js","dist-tags":{"latest":"0.7.0"},"versions":{"0.3.0":{"name":"@bodhveda/js","version":"0.3.0","keywords":["bodhveda","notification","sdk"],"license":"MIT","_id":"@bodhveda/js@0.3.0","maintainers":[{"name":"ceoshikhar","email":"ceoshikhar@gmail.com"}],"homepage":"https://github.com/MudgalLabs/bodhveda/tree/main/sdk/js/core#readme","bugs":{"url":"https://github.com/MudgalLabs/bodhveda/issues"},"dist":{"shasum":"9f8c7074df588263edefb7fcbe5bea3e1a5a3ebd","tarball":"https://registry.npmjs.org/@bodhveda/js/-/js-0.3.0.tgz","fileCount":35,"integrity":"sha512-gm9FWcJH4/PJl72adpzsMrcwCl6Uncjjvcks2a6mOSagMKiEIi4Jf6rUjEcB/yfExG8L2w25CEhBiSpBgZlYXA==","signatures":[{"sig":"MEUCIHRP4pOtc5Dl8KQCl3NZivJHFtkRf0zyV42+sbjvtVtpAiEAigtPOUsFIO2cknlRfU/MLYtSN1K7xTTBamXy9aS+hik=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":91814},"main":"dist/cjs/index.js","types":"dist/esm/index.d.ts","module":"dist/esm/index.js","exports":{".":{"import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"108ea297de783221211679f01ac58d4e1a7fb7b9","scripts":{"build":"npm run build:cjs && npm run build:esm","clean":"rm -rf dist","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"ceoshikhar","email":"ceoshikhar@gmail.com"},"repository":{"url":"git+https://github.com/MudgalLabs/bodhveda.git","type":"git","directory":"sdk/js/core"},"_npmVersion":"11.6.2","description":"JavaScript SDK for Bodhveda","directories":{},"_nodeVersion":"24.13.0","dependencies":{"axios":"^1.11.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.9.2"},"_npmOperationalInternal":{"tmp":"tmp/js_0.3.0_1784316631556_0.11388777979644593","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@bodhveda/js","version":"0.4.0","keywords":["bodhveda","notification","sdk"],"license":"MIT","_id":"@bodhveda/js@0.4.0","maintainers":[{"name":"ceoshikhar","email":"ceoshikhar@gmail.com"}],"homepage":"https://github.com/MudgalLabs/bodhveda/tree/main/sdk/js/core#readme","bugs":{"url":"https://github.com/MudgalLabs/bodhveda/issues"},"dist":{"shasum":"fa2cb3feb239b4e1ef98c3d476c44cd216f43818","tarball":"https://registry.npmjs.org/@bodhveda/js/-/js-0.4.0.tgz","fileCount":35,"integrity":"sha512-rfLV0wY0Sp7+RSEtUjUrNH1sauHUBKsR2kOm+bCtHb0izx3Voj9jjrrsmWdT2ZWk6wBHEY6AcKIMU3ztWMboBA==","signatures":[{"sig":"MEUCIQCJAZiuq5QUfFJ0uVViL6jZNkW0grmxbNsKzCkc2uJZwwIgO1mvk7ixPJQr+Uvr8xynLNTJPj7ksqY3DO8o6tSMfNk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":120073},"main":"dist/cjs/index.js","types":"dist/esm/index.d.ts","module":"dist/esm/index.js","exports":{".":{"import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"21ee7075d002082158c97dfb2f1f5c68589434e2","scripts":{"build":"npm run build:cjs && npm run build:esm","clean":"rm -rf dist","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"ceoshikhar","email":"ceoshikhar@gmail.com"},"repository":{"url":"git+https://github.com/MudgalLabs/bodhveda.git","type":"git","directory":"sdk/js/core"},"_npmVersion":"11.6.2","description":"JavaScript SDK for Bodhveda","directories":{},"_nodeVersion":"24.13.0","dependencies":{"axios":"^1.11.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.9.2"},"_npmOperationalInternal":{"tmp":"tmp/js_0.4.0_1784390622856_0.12301645669009709","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@bodhveda/js","version":"0.5.0","keywords":["bodhveda","notification","sdk"],"license":"MIT","_id":"@bodhveda/js@0.5.0","maintainers":[{"name":"ceoshikhar","email":"ceoshikhar@gmail.com"}],"homepage":"https://github.com/MudgalLabs/bodhveda/tree/main/sdk/js/core#readme","bugs":{"url":"https://github.com/MudgalLabs/bodhveda/issues"},"dist":{"shasum":"c8d70e415c87fb96254dfeb19aec65e087e951dc","tarball":"https://registry.npmjs.org/@bodhveda/js/-/js-0.5.0.tgz","fileCount":35,"integrity":"sha512-hcUi5XSKQqXsBrI8lCFHEfRKpuCFa9/W0b9c5Ogb5NDpK9CHj+D8ULK3tnK6srmIBs6PZekyrrQLp/QRtIXoYw==","signatures":[{"sig":"MEUCIQDqIBJEGjw+1i3NqFqy6zey/ErBkXbSr9kBIAVB6Aar8AIgR2Ksn+gHzYBk6kQz99nZjZ2Nf4Q36DSls83o18GkdXA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":129706},"main":"dist/cjs/index.js","types":"dist/esm/index.d.ts","module":"dist/esm/index.js","exports":{".":{"import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"8e4ce627e20666ca44cd1ec8d147780d3d69e9ce","scripts":{"build":"npm run build:cjs && npm run build:esm","clean":"rm -rf dist","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"ceoshikhar","email":"ceoshikhar@gmail.com"},"repository":{"url":"git+https://github.com/MudgalLabs/bodhveda.git","type":"git","directory":"sdk/js/core"},"_npmVersion":"11.6.2","description":"JavaScript SDK for Bodhveda","directories":{},"_nodeVersion":"24.13.0","dependencies":{"axios":"^1.16.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.9.2"},"_npmOperationalInternal":{"tmp":"tmp/js_0.5.0_1784488891898_0.6733965808819473","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"name":"@bodhveda/js","version":"0.6.0","keywords":["bodhveda","notification","sdk"],"license":"MIT","_id":"@bodhveda/js@0.6.0","maintainers":[{"name":"ceoshikhar","email":"ceoshikhar@gmail.com"}],"homepage":"https://github.com/MudgalLabs/bodhveda/tree/main/sdk/js/core#readme","bugs":{"url":"https://github.com/MudgalLabs/bodhveda/issues"},"dist":{"shasum":"b275c76a3284c403afa35112fbffffb6714eb599","tarball":"https://registry.npmjs.org/@bodhveda/js/-/js-0.6.0.tgz","fileCount":35,"integrity":"sha512-HU94pk5cVowfGFbLbzkjxy2LwkOhGAQ3Hn6K5HUS0za41Ea/kANOsykAr6psMGmKTu5M16NVHbhY+ZPrzaBlwA==","signatures":[{"sig":"MEUCICeTuTDrAuXPAnlQ5AHHWuWShZCpCBypsBhRVXfs4yAfAiEAkuj4PpDdnNarl/LuD/DwNgV/C+ARNw5eQU/u68ZUZAM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":132312},"main":"dist/cjs/index.js","types":"dist/esm/index.d.ts","module":"dist/esm/index.js","exports":{".":{"import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"e79b41c511e76eadaff503161b6bc2dd65bb2f89","scripts":{"build":"npm run build:cjs && npm run build:esm","clean":"rm -rf dist","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"ceoshikhar","email":"ceoshikhar@gmail.com"},"repository":{"url":"git+https://github.com/MudgalLabs/bodhveda.git","type":"git","directory":"sdk/js/core"},"_npmVersion":"11.6.2","description":"JavaScript SDK for Bodhveda","directories":{},"_nodeVersion":"24.13.0","dependencies":{"axios":"^1.16.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.9.2"},"_npmOperationalInternal":{"tmp":"tmp/js_0.6.0_1785193026350_0.40112246371729343","host":"s3://npm-registry-packages-npm-production"}},"0.7.0":{"name":"@bodhveda/js","description":"JavaScript SDK for Bodhveda","version":"0.7.0","license":"MIT","repository":{"type":"git","url":"git+https://github.com/MudgalLabs/bodhveda.git","directory":"sdk/js/core"},"keywords":["bodhveda","notification","sdk"],"publishConfig":{"access":"public"},"bugs":{"url":"https://github.com/MudgalLabs/bodhveda/issues"},"homepage":"https://github.com/MudgalLabs/bodhveda/tree/main/sdk/js/core#readme","main":"dist/cjs/index.js","module":"dist/esm/index.js","types":"dist/esm/index.d.ts","exports":{".":{"require":"./dist/cjs/index.js","import":"./dist/esm/index.js"}},"scripts":{"build":"npm run build:cjs && npm run build:esm","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","clean":"rm -rf dist","prepublishOnly":"npm run clean && npm run build"},"dependencies":{"axios":"^1.16.0"},"devDependencies":{"typescript":"^5.9.2"},"gitHead":"c87662a7dcb5321de743b4dbfc70bd5723c9eea7","_id":"@bodhveda/js@0.7.0","_nodeVersion":"24.13.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-7HNepJRamg9plcQ0JKKiBcptLDRwtppBaRL+KXYnPXFDoI8lt+ABJDP/hV9KGEhMSwj+X8pANUE1rRVw0lUgxg==","shasum":"c1a9dc5b840f8fa7de8c2b10a0e738deeb6aba26","tarball":"https://registry.npmjs.org/@bodhveda/js/-/js-0.7.0.tgz","fileCount":35,"unpackedSize":138382,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDjy8UkSfYw/T9xhUDvQjiSYKm+KjSnXWoGj2e2i7Y42wIhAM2uTaxNMICjDcONbzaH3OiGWwSww5cUtp8V0Jp6FZh4"}]},"_npmUser":{"name":"ceoshikhar","email":"ceoshikhar@gmail.com"},"directories":{},"maintainers":[{"name":"ceoshikhar","email":"ceoshikhar@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/js_0.7.0_1785789762548_0.9305695335051758"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-17T19:30:31.382Z","modified":"2026-08-03T20:42:42.884Z","0.3.0":"2026-07-17T19:30:31.703Z","0.4.0":"2026-07-18T16:03:43.024Z","0.5.0":"2026-07-19T19:21:32.029Z","0.6.0":"2026-07-27T22:57:06.489Z","0.7.0":"2026-08-03T20:42:42.688Z"},"bugs":{"url":"https://github.com/MudgalLabs/bodhveda/issues"},"license":"MIT","homepage":"https://github.com/MudgalLabs/bodhveda/tree/main/sdk/js/core#readme","keywords":["bodhveda","notification","sdk"],"repository":{"type":"git","url":"git+https://github.com/MudgalLabs/bodhveda.git","directory":"sdk/js/core"},"description":"JavaScript SDK for Bodhveda","maintainers":[{"name":"ceoshikhar","email":"ceoshikhar@gmail.com"}],"readme":"# JavaScript/TypeScript SDK for Bodhveda\n\nOfficial JavaScript/TypeScript SDK for Bodhveda.\n\nIt offers a simpler way to work with Bodhveda APIs in both browser and server environments.\n\n## Index\n\n-   [Installation](#installation)\n-   [Quick Start](#quick-start)\n-   [Notifications](#notifications)\n-   [Recipients](#recipients)\n    -   [Recipient Notifications](#recipient-notifications)\n    -   [Recipient Preferences](#recipient-preferences)\n    -   [Recipient Contacts](#recipient-contacts)\n-   [Project Preferences](#project-preferences)\n-   [License](#license)\n\n## Installation\n\n```bash\nnpm install @bodhveda/js\n```\n\n> Previously published as `bodhveda`. That package is deprecated — install `@bodhveda/js` instead.\n\n## Quick Start\n\n```typescript\nimport { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"YOUR_API_KEY\");\n\n// Send a notification to a recipient.\n// Note: Bodhveda will create the recipient if it does not already exist.\nawait bodhveda.notifications.send({\n    recipient_id: \"user-123\",\n    payload: { message: \"Hello, world!\" },\n});\n\n// List all notifications for a recipient.\nconst notifications = await bodhveda.recipients.notifications.list(\"user-123\");\n```\n\n## Notifications\n\n### Send a notification\n\nSend a notification to a recipient or broadcast to a target.\n\n```typescript\nawait bodhveda.notifications.send({\n    recipient_id: \"user-123\",\n    payload: { message: \"Hello, world!\" },\n});\n```\n\n### Send with email\n\nInclude the optional `email` block to also send an email. Its presence makes email\neligible (**direct sends only** — an email block on a broadcast returns `400`).\nBodhveda does no templating: you render the subject/HTML/text yourself (e.g. with\n`@react-email`) and pass the result. `text` is optional and derived from `html`.\n\nEmail fires only when the `(target, email)` pair is cataloged, the recipient's email\npreference is enabled, and the recipient has a primary email\n[contact](#recipient-contacts).\n\n> [!IMPORTANT]\n> **Email needs the `(target, email)` pair cataloged** — not because of a gate, but\n> because `email` is not a medium anything defaults to. Without a project preference\n> for the pair, the email resolves to not-delivered and the delivery row records\n> `not_cataloged`. Catalog it with\n> [`upsertMany`](#set-up-a-whole-catalog-at-once).\n>\n> A `topic: any` catalog entry covers every concrete topic beneath it, so one entry\n> covers an unbounded set of runtime-generated targets — you never need one entry per\n> resource id.\n>\n> If your project has **strict targets** turned on (a per-project setting, off by\n> default), an uncataloged pair is a `400` at send time instead: the send is rejected\n> and nothing is written. The check applies per medium to the mediums a send actually\n> asks for — a send carrying only `payload` needs an `in_app` entry, one carrying\n> `email` needs an `email` entry, one carrying both needs both. With it on, seed the\n> catalog **before the new code starts serving**, on every deploy.\n>\n> Cataloged or not, email still needs the recipient's email preference enabled and\n> a primary email contact. Those resolve asynchronously, so read the outcome back\n> rather than inferring it from the send:\n>\n> ```typescript\n> const n = await bodhveda.notifications.get(res.notification.id);\n> console.log(n.email?.status, n.email?.failure_reason);\n> ```\n\n```typescript\nconst res = await bodhveda.notifications.send({\n    recipient_id: \"user-123\",\n    target: { channel: \"digest\", topic: \"none\", event: \"sent\" },\n    payload: { title: \"Your daily digest is ready.\" },\n    email: {\n        subject: \"Your daily digest\",\n        html: \"<h1>Your daily digest</h1><p>3 new follow-ups today.</p>\",\n    },\n});\n// res.notification.id — the send is accepted (status \"enqueued\"); the email is\n// resolved asynchronously. Read the outcome back with notifications.get() below.\n```\n\n### Get a notification (check the outcome)\n\nThe send is **asynchronous**: it accepts the notification and returns its id\n(`status: \"enqueued\"`), then the worker resolves in-app delivery and the email.\nFetch the notification by id to see the resolved in-app `status` and, when the send\nincluded an `email` block, the email delivery outcome on `notification.email`.\n\n```typescript\nconst notification = await bodhveda.notifications.get(res.notification!.id);\n\nnotification.status; // \"delivered\" | \"muted\" | \"quota_exceeded\" | \"failed\" | \"enqueued\"\nnotification.email?.status; // \"pending\" | \"sent\" | \"delivered\" | \"bounced\" | ...\nnotification.email?.delivered_at;\n```\n\n---\n\n## Recipients\n\n### Create a recipient\n\nCreate a new recipient.\n\n```typescript\nawait bodhveda.recipients.create({\n    id: \"user-123\",\n    name: \"Alice\",\n});\n```\n\n### Create multiple recipients (batch)\n\nCreate multiple recipients in a single request.\n\n```typescript\nawait bodhveda.recipients.createBatch({\n    recipients: [\n        { id: \"user-1\", name: \"Alice\" },\n        { id: \"user-2\", name: \"Bob\" },\n    ],\n});\n```\n\n### Get a recipient\n\nRetrieve details of a recipient by ID.\n\n```typescript\nconst recipient = await bodhveda.recipients.get(\"user-123\");\n```\n\n### Update a recipient\n\nUpdate recipient details.\n\n```typescript\nawait bodhveda.recipients.update(\"user-123\", { name: \"Alice Updated\" });\n```\n\n### Delete a recipient\n\nDelete a recipient by ID.\n\n```typescript\nawait bodhveda.recipients.delete(\"user-123\");\n```\n\n---\n\n## Recipient Notifications\n\n### List notifications\n\nList notifications for a recipient.\n\n```typescript\nconst notifications = await bodhveda.recipients.notifications.list(\"user-123\");\n```\n\n### Get unread notification count\n\nGet the count of unread notifications for a recipient.\n\n```typescript\nconst { unread_count } = await bodhveda.recipients.notifications.unreadCount(\n    \"user-123\"\n);\n```\n\n### Update notification state\n\nUpdate the state (e.g., mark as read) of notifications for a recipient.\n\n```typescript\nawait bodhveda.recipients.notifications.updateState(\"user-123\", {\n    ids: [1, 2, 3],\n    state: { read: true },\n});\n```\n\n### Delete notifications\n\nDelete notifications for a recipient.\n\n```typescript\nawait bodhveda.recipients.notifications.delete(\"user-123\", {\n    ids: [1, 2, 3],\n});\n```\n\n---\n\n## Recipient Preferences\n\n### List preferences\n\nList all preferences for a recipient.\n\n```typescript\nconst preferences = await bodhveda.recipients.preferences.list(\"user-123\");\n```\n\n### Set a preference\n\nSet a notification preference for a recipient. Pass an optional `medium`\n(`\"in_app\"` or `\"email\"`) to toggle in-app and email independently for the same\ntarget. It defaults to `\"in_app\"` when omitted.\n\n```typescript\nawait bodhveda.recipients.preferences.set(\"user-123\", {\n    target: { channel: \"digest\", topic: \"none\", event: \"sent\" },\n    medium: \"email\",\n    state: { enabled: true },\n});\n```\n\n### Check a preference\n\nCheck the state of a specific preference for a recipient.\n\n```typescript\nconst result = await bodhveda.recipients.preferences.check(\"user-123\", {\n    target: { channel: \"digest\", topic: \"none\", event: \"sent\" },\n    medium: \"email\",\n});\n```\n\n---\n\n## Recipient Contacts\n\nContacts are per-medium addresses for a recipient. To send **email** to a recipient,\nadd an `email` contact and mark it primary. Sync this **server-side** (e.g. on your\n`/me` endpoint) so the address never rides a browser request.\n\n`create`, `list`, and `update` work with a `Full access` or `Recipient access` API\nkey; `delete` requires `Full access`.\n\n### Add a contact\n\n```typescript\nawait bodhveda.recipients.contacts.create(\"user-123\", {\n    medium: \"email\",\n    address: \"alice@example.com\",\n    is_primary: true,\n});\n```\n\n`create` is strict — it rejects with a `409` when the contact already exists. To\nkeep a recipient's primary address current from a server without a\nlist-then-diff, use `setPrimary` instead.\n\n### Set the primary contact\n\nIdempotently ensure an address is the recipient's **primary** contact for a\nmedium — create it if absent, update the existing primary in place if the\naddress differs (which resets verification), or no-op if it already matches.\nReturns the resulting primary contact (`200`) in every case, so a \"keep the\nprimary email current\" sync is a single call.\n\n```typescript\nawait bodhveda.recipients.contacts.setPrimary(\"user-123\", {\n    medium: \"email\",\n    address: \"alice@example.com\",\n});\n```\n\n### List contacts\n\n```typescript\nconst { contacts } = await bodhveda.recipients.contacts.list(\"user-123\");\n```\n\n### Update a contact\n\n```typescript\nawait bodhveda.recipients.contacts.update(\"user-123\", 1, {\n    address: \"alice.new@example.com\",\n});\n```\n\n### Delete a contact\n\nRequires a `Full access` API key.\n\n```typescript\nawait bodhveda.recipients.contacts.delete(\"user-123\", 1);\n```\n\n---\n\n## Project Preferences\n\nThe **project preference catalog** declares which `(target, medium)` pairs your\nproject may send, and the default a recipient inherits until they set a\npreference of their own. This is different from\n[Recipient Preferences](#recipient-preferences), which are a single recipient's\nown toggles — manage the catalog with `bodhveda.preferences`.\n\nRequires a `Full access` API key, and is a **server-side** concern. The project\nis taken from the API key.\n\n> [!IMPORTANT]\n> **Seed the catalog from your deploy, not by hand.**\n>\n> Your desired catalog lives in your code and the real one lives in Bodhveda, so the\n> two drift the moment you ship an event and forget to update Bodhveda to match. An\n> uncataloged target has no preference surface — it appears on no settings screen, so\n> no recipient can mute it — and email to it never sends at all.\n>\n> Derive the array from whatever already defines your events, and run\n> [`upsertMany`](#set-up-a-whole-catalog-at-once) from your deploy pipeline on **every**\n> deploy. It is an idempotent merge, so a deploy that changes nothing is a no-op.\n>\n> This matters more once **strict targets** is on for the project: an uncataloged\n> target then 400s outright. Seed **before** the new code starts serving, and let a\n> failed seed **fail the deploy** — stopping leaves the previous version running, which\n> only sends targets that are already cataloged.\n\n### List the catalog\n\n```typescript\nconst catalog = await bodhveda.preferences.list();\n```\n\n### Create a catalog entry\n\nStrict — rejects with a `409` when an entry for the same\n`(channel, topic, event, medium)` already exists. `medium` defaults to `in_app`\nwhen omitted.\n\n```typescript\nconst entry = await bodhveda.preferences.create({\n    channel: \"digest\",\n    topic: \"none\",\n    event: \"sent\",\n    medium: \"email\",\n    name: \"Daily digest\",\n    description: \"Receive a daily summary of activity.\",\n    default_enabled: true,\n});\n```\n\n### Get / update / delete an entry\n\nThe natural key (`channel`/`topic`/`event`/`medium`) is immutable — `update`\nchanges only the name, description and default.\n\n```typescript\nconst entry = await bodhveda.preferences.get(123);\n\nawait bodhveda.preferences.update(123, {\n    name: \"Weekly digest\",\n    description: \"Receive a weekly summary of activity.\",\n    default_enabled: false,\n});\n\n// Un-catalogs the (target, medium).\nawait bodhveda.preferences.delete(123);\n```\n\n### Set up a whole catalog at once\n\n`upsertMany` declaratively merges an entire catalog in one call — the primitive\nfor a one-off \"set up my project's preferences\" script. Each item is upserted by\nits natural key (new inserted, existing name + description + default updated).\nEntries **not** in the array are left untouched.\n\n```typescript\nawait bodhveda.preferences.upsertMany([\n    { channel: \"digest\", topic: \"none\", event: \"sent\", medium: \"email\", name: \"Daily digest\", default_enabled: true },\n    { channel: \"posts\", topic: \"any\", event: \"new_comment\", medium: \"email\", name: \"Comments\", default_enabled: true },\n]);\n```\n\nPass `{ prune: true }` to also **delete** entries absent from the array, making\nit the entire desired catalog. Pruning un-catalogs a `(target, medium)`, which\nturns a non-`in_app` medium off for recipients relying on the catalog default —\nso it is opt-in.\n\n```typescript\nawait bodhveda.preferences.upsertMany(desiredCatalog, { prune: true });\n```\n\n---\n\n## License\n\nMIT\n","readmeFilename":"README.md"}