{"_id":"@aamsdn/trail-pulse","name":"@aamsdn/trail-pulse","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@aamsdn/trail-pulse","version":"0.1.0","description":"Official Node.js and TypeScript SDK for TrailPulse event ingestion","keywords":["trailpulse","analytics","events","observability","typescript"],"license":"MIT","type":"module","sideEffects":false,"engines":{"node":">=18.17"},"main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"}},"scripts":{"clean":"rm -rf dist","build":"tsc -p tsconfig.json","typecheck":"tsc -p tsconfig.json --noEmit","test":"vitest run --config vitest.config.ts","prepublishOnly":"npm run clean && npm run test && npm run build"},"publishConfig":{"access":"public"},"_id":"@aamsdn/trail-pulse@0.1.0","gitHead":"2e29d09010e6e1bd810588ac59157bbdaca7885b","_nodeVersion":"20.20.2","_npmVersion":"10.8.2","dist":{"integrity":"sha512-XUKx7IHpoTN/FZ7Nh3KvdA97uN66PJOP91b76/jdEmk/hXbz0doArroVur2bzrqOpF3gdOL1Q6GsWuTnQZFUrA==","shasum":"f833a22a2d8bdde117d857267dc57dcbe29bbd39","tarball":"https://registry.npmjs.org/@aamsdn/trail-pulse/-/trail-pulse-0.1.0.tgz","fileCount":7,"unpackedSize":27102,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQD5o+4w5NCujOe1qwb6fLbvrw8R35PCav+dO/QvDg1FOQIhAM+6rPtmdMnBKPRCV1rK9ZSkyXmMuCeLow0y80RNVW4A"}]},"_npmUser":{"name":"ahmedalimohasoliman","email":"ahmedalimohasoliman@gmail.com"},"directories":{},"maintainers":[{"name":"ahmedalimohasoliman","email":"ahmedalimohasoliman@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/trail-pulse_0.1.0_1784982270525_0.3192767273584547"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-25T12:24:30.352Z","0.1.0":"2026-07-25T12:24:30.701Z","modified":"2026-07-25T12:24:30.969Z"},"maintainers":[{"name":"ahmedalimohasoliman","email":"ahmedalimohasoliman@gmail.com"}],"description":"Official Node.js and TypeScript SDK for TrailPulse event ingestion","keywords":["trailpulse","analytics","events","observability","typescript"],"license":"MIT","readme":"# `@aamsdn/trail-pulse`\n\nOfficial zero-dependency Node.js and TypeScript SDK for sending server-side events to TrailPulse.\n\n## Requirements\n\n- Node.js 18.17 or newer\n- A TrailPulse server API key (`tp_live_…` or `tp_test_…`)\n- A publicly reachable TrailPulse deployment when QStash delivery is enabled\n\nNever expose a server key in browser code, logs, source control, or a `NEXT_PUBLIC_` environment variable.\n\n## Install\n\n```bash\nnpm install @aamsdn/trail-pulse\n```\n\nUntil the first npm release, install from a local checkout:\n\n```bash\n# Run this once in the TrailPulse repository:\nnpm run sdk:node:build\n\n# Then run this in the consuming application:\nnpm install ../trail-pulse/packages/node\n```\n\n## Create a client\n\n```ts\nimport { TrailPulse } from \"@aamsdn/trail-pulse\";\n\nexport const trailPulse = new TrailPulse({\n  endpoint: process.env.TRAILPULSE_URL!,\n  secretKey: process.env.TRAILPULSE_SECRET_KEY!,\n  timeoutMs: 5_000,\n  retries: 3\n});\n```\n\nCreate one client and reuse it. The SDK never logs your key and has no runtime dependencies.\n\n## Track events\n\n```ts\nconst result = await trailPulse.track({\n  event: \"project.created\",\n  userId: \"user_123\",\n  sessionId: \"session_456\",\n  properties: {\n    projectId: \"project_789\",\n    plan: \"personal\"\n  },\n  context: {\n    service: \"projects-api\",\n    region: \"dubai\"\n  }\n});\n\nconsole.log(result.eventId);\n```\n\nFor simpler events:\n\n```ts\nawait trailPulse.track(\"job.completed\", {\n  jobId: \"job_42\",\n  durationMs: 842\n});\n```\n\nEvent names must be lowercase and dot-separated, such as `checkout.completed` or `api.request.failed`.\n\n## Send a batch\n\n```ts\nawait trailPulse.batch([\n  { event: \"job.started\", properties: { jobId: \"job_42\" } },\n  { event: \"job.completed\", properties: { jobId: \"job_42\", durationMs: 842 } }\n]);\n```\n\nBatches contain 1–100 events. TrailPulse returns only after the batch has been accepted by QStash, normally with HTTP `202`; aggregation continues asynchronously.\n\n## Identify a user\n\n```ts\nawait trailPulse.identify(\"user_123\", {\n  plan: \"team\",\n  role: \"owner\"\n});\n```\n\nAvoid sending passwords, access tokens, payment data, or unnecessary personal information. Configure blocked property paths on the TrailPulse application as a second layer of protection.\n\n## Stable event IDs and idempotency\n\nSupply a stable event `id` when the caller may retry the same logical operation:\n\n```ts\nawait trailPulse.track({\n  id: `evt_order_${order.id}`,\n  event: \"order.completed\",\n  userId: order.userId,\n  properties: { orderId: order.id, total: order.total }\n}, {\n  idempotencyKey: `order.completed:${order.id}`\n});\n```\n\nThe processor deduplicates by event ID. The SDK also sends `Idempotency-Key` for forward compatibility.\n\n## Error handling\n\n```ts\nimport { TrailPulseError } from \"@aamsdn/trail-pulse\";\n\ntry {\n  await trailPulse.track({ event: \"deployment.completed\" });\n} catch (error) {\n  if (error instanceof TrailPulseError) {\n    console.error({\n      code: error.code,\n      status: error.status,\n      retryable: error.retryable,\n      message: error.message\n    });\n  }\n}\n```\n\nThe SDK retries network failures, timeouts, `408`, `425`, `429`, and selected `5xx` responses with bounded exponential backoff and jitter. It honors `Retry-After` up to 30 seconds. Validation and other non-retryable `4xx` responses fail immediately.\n\n## Next.js server example\n\n```ts\n// lib/trailpulse.ts\nimport \"server-only\";\nimport { TrailPulse } from \"@aamsdn/trail-pulse\";\n\nexport const trailPulse = new TrailPulse({\n  endpoint: process.env.TRAILPULSE_URL!,\n  secretKey: process.env.TRAILPULSE_SECRET_KEY!\n});\n```\n\n```ts\n// app/api/projects/route.ts\nimport { after } from \"next/server\";\nimport { trailPulse } from \"@/lib/trailpulse\";\n\nexport async function POST(request: Request) {\n  const project = await createProject(await request.json());\n\n  after(() => trailPulse.track({\n    event: \"project.created\",\n    userId: project.ownerId,\n    properties: { projectId: project.id }\n  }));\n\n  return Response.json({ project }, { status: 201 });\n}\n```\n\nUse `after()` for non-critical product analytics. Await `track()` when knowing that QStash accepted the event is part of the operation’s reliability requirement.\n\n## Express example\n\n```ts\napp.post(\"/orders\", async (req, res, next) => {\n  try {\n    const order = await createOrder(req.body);\n    await trailPulse.track({\n      event: \"order.created\",\n      userId: order.userId,\n      properties: { orderId: order.id }\n    });\n    res.status(201).json(order);\n  } catch (error) {\n    next(error);\n  }\n});\n```\n\n## Configuration\n\n| Option | Default | Description |\n| --- | --- | --- |\n| `endpoint` | required | TrailPulse origin, for example `https://trail.example.com` |\n| `secretKey` | required | Server ingestion key; never logged by the SDK |\n| `timeoutMs` | `5000` | Timeout per HTTP attempt |\n| `retries` | `3` | Additional attempts, from 0 through 10 |\n| `fetch` | Node global fetch | Optional compatible fetch implementation, useful in tests |\n| `userAgent` | SDK name/version | Optional user-agent identifier |\n\nIndividual requests accept an optional `AbortSignal` and `idempotencyKey`.\n\n## Publishing\n\nFrom this repository:\n\n```bash\nnpm run sdk:node:check\nnpm run sdk:node:pack\nnpm run sdk:node:publish\n```\n\nPublishing requires npm access to the `@aamsdn` organization. `npm publish` runs the package tests and build automatically. See [`PUBLISHING.md`](PUBLISHING.md) for the complete release checklist.\n","readmeFilename":"README.md","_rev":"1-3ef1e233f5b2db50123717fa59cc1235"}