{"_id":"@a.svetlitskiy/yandex-metrika","_rev":"2-48c3a094715340af0d4726cd39ffcca0","name":"@a.svetlitskiy/yandex-metrika","dist-tags":{"latest":"0.4.0"},"versions":{"0.3.0":{"name":"@a.svetlitskiy/yandex-metrika","version":"0.3.0","keywords":["analytics","measurement-protocol","metrica","typescript","yandex","yandex-metrica"],"author":{"name":"Aleksey Svetlitskiy"},"license":"MIT","_id":"@a.svetlitskiy/yandex-metrika@0.3.0","maintainers":[{"name":"a.svetlitskiy","email":"a.svetlitskiy@gmail.com"}],"homepage":"https://github.com/svetlitskiy/yandex-metrika#readme","bugs":{"url":"https://github.com/svetlitskiy/yandex-metrika/issues"},"dist":{"shasum":"183f1c87a8307f9cebecb98947453f83de661956","tarball":"https://registry.npmjs.org/@a.svetlitskiy/yandex-metrika/-/yandex-metrika-0.3.0.tgz","fileCount":32,"integrity":"sha512-1AcmvlxVE1VjfVA5YavQqr3zFvTdfrV4/d4SMYoOgQcBYhBf1Nr7e5hNXcH9K3ATMyyxY6IIRi69Wlqp+5ZPeA==","signatures":[{"sig":"MEYCIQDV8Uygfgorjbig76wDg1ojeyWA30wcIYjEhFWGb0YptQIhAIJuUorZsueY/B2AhN4TO4yo4D/R0qaNiyWTWix8suIE","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":125942},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.18.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./types":{"import":{"types":"./dist/types.d.ts","default":"./dist/types.js"},"require":{"types":"./dist/types.d.cts","default":"./dist/types.cjs"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.cts","default":"./dist/server/index.cjs"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.cts","default":"./dist/browser/index.cjs"}},"./package.json":"./package.json"},"gitHead":"e432c29ca90f064e29b206dc7081fca80948882d","scripts":{"test":"vitest run","build":"tsup","check":"npm run format:check && npm run typecheck && npm test && npm run build && npm run check:package","format":"prettier --write .","typecheck":"tsc --noEmit","test:watch":"vitest","format:check":"prettier --check .","check:package":"publint --pack=false && node scripts/check-package.mjs","prepublishOnly":"npm run check"},"_npmUser":{"name":"a.svetlitskiy","email":"a.svetlitskiy@gmail.com"},"repository":{"url":"git+https://github.com/svetlitskiy/yandex-metrika.git","type":"git"},"_npmVersion":"10.8.2","description":"Framework-neutral Yandex Metrica SDK for browser goals and server-side Measurement Protocol events","directories":{},"sideEffects":false,"_nodeVersion":"20.19.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.4.0","vitest":"^3.1.1","publint":"^0.3.12","prettier":"^3.5.3","typescript":"^5.8.3","@types/node":"^22.15.0","@arethetypeswrong/cli":"^0.18.2"},"_npmOperationalInternal":{"tmp":"tmp/yandex-metrika_0.3.0_1788056400069_0.13412643805572566","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"_id":"@a.svetlitskiy/yandex-metrika@0.4.0","bugs":{"url":"https://github.com/svetlitskiy/yandex-metrika/issues"},"dist":{"shasum":"9c167a0e0566460b28fb4a1e430865123fb0adc6","tarball":"https://registry.npmjs.org/@a.svetlitskiy/yandex-metrika/-/yandex-metrika-0.4.0.tgz","fileCount":32,"integrity":"sha512-8SJiQ+0M/Vx+eBOV463UVnm1tU3mo+aISrUtBEbZuKGlTfA7uIHNkwtxvg1EtONXDd2NcJ8tc7l1ejQzjbQBqQ==","signatures":[{"sig":"MEQCIEvxSWLBbpPLQcB+kR73dVkQOrcBHrwUEhm7kM7EKAMdAiBAYQPycIUxnYiYUXg4MgeFza5v2TeSksoL9CMbvLZNCg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCICKOgtXJrYN/tmBUOXIJTXc4KuqV+mo/ofSQFKKlbJ4VAiEAoYFtbr98GwbPc/PJT9Xo3l+VJMaldeBN4V5K/Ok1yos="}],"unpackedSize":126792},"main":"./dist/index.cjs","name":"@a.svetlitskiy/yandex-metrika","type":"module","types":"./dist/index.d.ts","author":{"name":"Aleksey Svetlitskiy"},"module":"./dist/index.js","engines":{"node":">=18.18.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./types":{"import":{"types":"./dist/types.d.ts","default":"./dist/types.js"},"require":{"types":"./dist/types.d.cts","default":"./dist/types.cjs"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.cts","default":"./dist/server/index.cjs"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.cts","default":"./dist/browser/index.cjs"}},"./package.json":"./package.json"},"gitHead":"dd967577ae5282fc1eb813ebf95ff831a772531d","license":"MIT","scripts":{"test":"vitest run","build":"tsup","check":"npm run format:check && npm run typecheck && npm test && npm run build && npm run check:package","format":"prettier --write .","typecheck":"tsc --noEmit","test:watch":"vitest","format:check":"prettier --check .","check:package":"publint --pack=false && node scripts/check-package.mjs","prepublishOnly":"npm run check"},"version":"0.4.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:60b8ba6c-d329-4cbc-b7bc-04f611eeb32b"}},"homepage":"https://github.com/svetlitskiy/yandex-metrika#readme","keywords":["analytics","measurement-protocol","metrica","typescript","yandex","yandex-metrica"],"repository":{"url":"git+https://github.com/svetlitskiy/yandex-metrika.git","type":"git"},"_npmVersion":"11.19.0","description":"Framework-neutral Yandex Metrica SDK for browser goals and server-side Measurement Protocol events","directories":{},"maintainers":[{"name":"a.svetlitskiy","email":"a.svetlitskiy@gmail.com"}],"sideEffects":false,"_nodeVersion":"24.21.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.4.0","vitest":"^3.1.1","publint":"^0.3.12","prettier":"^3.5.3","typescript":"^5.8.3","@types/node":"^22.15.0","@arethetypeswrong/cli":"^0.18.2"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/yandex-metrika_0.4.0_1790521791843_0.0335492909798536"}}},"time":{"created":"2026-08-30T02:19:59.845Z","modified":"2026-09-27T15:09:52.059Z","0.3.0":"2026-08-30T02:20:00.202Z","0.4.0":"2026-09-27T15:09:51.924Z"},"bugs":{"url":"https://github.com/svetlitskiy/yandex-metrika/issues"},"author":{"name":"Aleksey Svetlitskiy"},"license":"MIT","homepage":"https://github.com/svetlitskiy/yandex-metrika#readme","keywords":["analytics","measurement-protocol","metrica","typescript","yandex","yandex-metrica"],"repository":{"url":"git+https://github.com/svetlitskiy/yandex-metrika.git","type":"git"},"description":"Framework-neutral Yandex Metrica SDK for browser goals and server-side Measurement Protocol events","maintainers":[{"name":"a.svetlitskiy","email":"a.svetlitskiy@gmail.com"}],"readme":"# @a.svetlitskiy/yandex-metrika\n\n[![npm version](https://img.shields.io/npm/v/@a.svetlitskiy/yandex-metrika.svg)](https://www.npmjs.com/package/@a.svetlitskiy/yandex-metrika)\n[![CI](https://github.com/svetlitskiy/yandex-metrika/actions/workflows/ci.yml/badge.svg)](https://github.com/svetlitskiy/yandex-metrika/actions/workflows/ci.yml)\n[![license](https://img.shields.io/npm/l/@a.svetlitskiy/yandex-metrika.svg)](./LICENSE)\n\nA small, framework-neutral TypeScript SDK for sending custom events to\n[Yandex Metrica](https://metrica.yandex.com/) from both the browser and the\nserver.\n\nUse the browser entry point for the standard `window.ym` API. Use the server\nentry point for events that happen after the browser request: completed\npayments, webhooks, background jobs, CRM updates, bots, or other server-side\nworkflows.\n\n## Why this package exists\n\nThe Yandex Metrica browser API is convenient for UI interactions, but many\nimportant conversions do not finish in a browser. Yandex Metrica's Measurement\nProtocol can report those conversions from a server, but it has a different\nHTTP API and requires careful handling of ClientID values and secret tokens.\n\nThis package provides one typed, dependency-free transport layer for both\nenvironments while keeping application policy in your application:\n\n- browser calls are SSR-safe and become no-ops until `window.ym` is available;\n- server calls use the official Measurement Protocol collect endpoint;\n- browser and server code are exposed through separate package entry points;\n- the package does not depend on React, Next.js, Express, Fastify, or a database;\n- the package never invents a ClientID or decides how long your application\n  should store it.\n\nMeasurement Protocol supplements the regular Metrica tag; it is not a complete\nreplacement for it.\n\n## Installation\n\n```bash\nnpm install @a.svetlitskiy/yandex-metrika\n```\n\nThe package ships ESM, CommonJS, and TypeScript declarations. It has no runtime\ndependencies and supports Node.js 18.18 or newer.\n\n## Package entry points\n\n| Import                                  | Purpose                                 |\n| --------------------------------------- | --------------------------------------- |\n| `@a.svetlitskiy/yandex-metrika/browser` | The `window.ym` browser client          |\n| `@a.svetlitskiy/yandex-metrika/server`  | Server-side Measurement Protocol events |\n| `@a.svetlitskiy/yandex-metrika`         | Shared helpers and types                |\n| `@a.svetlitskiy/yandex-metrika/types`   | Explicit type-only imports              |\n\n## Browser quick start\n\nLoading and configuring the Yandex Metrica tag remains the host application's\nresponsibility. Once the tag has created `window.ym`, create a client:\n\n```ts\nimport {\n  createMetrikaBrowserClient,\n  parseMetrikaTagId,\n} from \"@a.svetlitskiy/yandex-metrika/browser\";\n\nconst metrika = createMetrikaBrowserClient({\n  tagId: () => parseMetrikaTagId(process.env.NEXT_PUBLIC_YANDEX_METRIKA_ID),\n});\n\nmetrika.reachGoal(\"signup\", { plan: \"pro\" });\nmetrika.hit(window.location.href, { params: { section: \"account\" } });\nmetrika.setUserID(\"user-123\");\nmetrika.userParams({ plan: \"pro\" });\n```\n\nImporting this module does not access `window`. Calls are safe during SSR and\nbecome no-ops when the counter ID or `window.ym` is unavailable.\n\nSee [Browser usage](./docs/browser.md) for ClientID capture and framework notes.\n\n## Server quick start\n\nFirst enable Measurement Protocol in the counter's **Data security and usage**\nsettings and create a secret token. Never expose that token to browser code.\n\n```ts\nimport { createMetrikaServerClient } from \"@a.svetlitskiy/yandex-metrika/server\";\n\nconst metrika = createMetrikaServerClient({\n  counterId: process.env.YANDEX_METRIKA_COUNTER_ID,\n  measurementToken: process.env.YANDEX_METRIKA_MEASUREMENT_TOKEN,\n});\n\nconst result = await metrika.reachGoal({\n  clientId: storedMetrikaClientId,\n  name: \"purchase\",\n  url: \"https://example.com/checkout/success\",\n  params: { orderId: \"order-123\", plan: \"pro\" },\n});\n\n// \"sent\" | \"skipped_unconfigured\" | \"skipped_no_client_id\"\n```\n\nThe method throws when a configured collect request fails. Missing credentials\nand ClientID values return explicit skip results, making optional analytics easy\nto integrate without masking transport failures.\n\nSee [Server usage](./docs/server.md) for new visits, event timestamps, injected\n`fetch`, and error handling.\n\n## Connecting browser and server events\n\nMeasurement Protocol uses the Metrica ClientID to associate a server event with\na visitor. How that value reaches the backend depends on where the backend runs,\nso the package supports the three realistic cases without requiring a dedicated\nanalytics endpoint.\n\n### Same domain: read it from the incoming request\n\nWhen the backend shares a registrable domain with the site, the Metrica\n`_ym_uid` cookie arrives with ordinary business requests:\n\n```ts\nimport { getMetrikaClientIdFromRequest } from \"@a.svetlitskiy/yandex-metrika/server\";\n\nconst clientId = getMetrikaClientIdFromRequest(request);\n```\n\nThe helper accepts a fetch `Request`, a Node.js `IncomingMessage`, or any object\nwith headers. `getMetrikaClientIdFromHeaders` and `getMetrikaClientIdFromCookie`\nare available when only headers or only a cookie string are at hand.\n\n### Different domains: propagate an explicit header\n\nA browser does not send `example.com` cookies to `api.other-service.io`. Attach\nthe ClientID to the business request you already make:\n\n```ts\nawait fetch(\"https://api.other-service.io/orders\", {\n  method: \"POST\",\n  headers: {\n    \"Content-Type\": \"application/json\",\n    ...(await metrika.getPropagationHeaders()),\n  },\n  body: JSON.stringify(order),\n});\n```\n\n`getPropagationHeaders` resolves to `{ \"X-Yandex-Metrika-Client-Id\": \"...\" }`, or\nto an empty object when no ClientID is available, so spreading it is always\nsafe. The server helpers read that header before falling back to the cookie.\n\nThe package never patches global `fetch` and never sends the ClientID anywhere\non its own. The application decides which requests carry the header.\n\n### Delayed events: persist the ClientID\n\nPayment webhooks, background jobs, and CRM updates run when no browser request\nexists. Store the extracted value with the entity the later event belongs to:\n\n```ts\norder.metrikaClientId = getMetrikaClientIdFromRequest(request);\n\n// later, from a webhook:\nawait metrika.reachGoal({\n  clientId: order.metrikaClientId,\n  name: \"payment_completed\",\n  url: \"https://example.com/orders/payment\",\n});\n```\n\n### Optional: a dedicated synchronization endpoint\n\nA separate endpoint is still useful when the ClientID must be attached to a\nsigned-in user before any business request happens:\n\n```ts\nconst clientId = await metrika.getClientID();\n```\n\nSend that value wherever your application needs it. This is one option, not the\nrequired integration path.\n\nYour application decides how the ClientID maps to a user, where it is stored,\nwhen it expires, and whether analytics consent permits storing it. A propagated\nClientID is untrusted input; the helpers validate it with `isMetrikaClientId`,\nand it must never be treated as authentication or authorization data.\n\n## Starting a server-side visit\n\nIf an event cannot extend a recent browser visit, send a pageview immediately\nbefore the goal:\n\n```ts\nawait metrika.reachGoal({\n  clientId: storedMetrikaClientId,\n  name: \"subscription_renewed\",\n  url: \"https://example.com/_channel/background-job\",\n  pageview: {\n    referrer: \"https://example.com\",\n    title: \"Background job\",\n  },\n});\n```\n\nThe pageview metadata is required because Yandex Metrica requires `dr`, `dl`,\nand `dt` when creating a pageview. Measurement Protocol accepts events up to 12\nhours in the past; use `eventTime` only within that window.\n\n## What the package does not do\n\n- It does not load or configure Yandex's browser tag.\n- It does not store ClientID or UserID values.\n- It does not manage consent, cookies, authentication, or user mapping.\n- It does not retry failed requests or hide HTTP errors.\n- It does not define goal names or application-specific event schemas.\n- It does not send secret Measurement Protocol credentials from the browser.\n\nThese boundaries keep the package reusable and prevent analytics transport from\nsilently making product or privacy decisions.\n\n## Security and privacy\n\n- Import Measurement Protocol code only from the `/server` entry point.\n- Keep `measurementToken` in a server-only secret store.\n- Do not put personal data or secrets into goal parameters.\n- Treat ClientID as user-associated analytics data and apply your consent and\n  retention policy.\n- Rotate the Measurement Protocol token if it is ever exposed.\n\nPlease report package vulnerabilities according to [SECURITY.md](./SECURITY.md).\n\n## Documentation\n\n- [Browser usage](./docs/browser.md)\n- [Server and Measurement Protocol usage](./docs/server.md)\n- [Contributing](./CONTRIBUTING.md)\n- [Release process](./docs/releasing.md)\n\n## License\n\n[MIT](./LICENSE)\n","readmeFilename":"README.md"}