npm package: @myvitalrx/activity-publisher
Description: Event publisher and logger for Node.js and AWS Lambda, supporting SQS and file transports.
Author: Prasanth M S prasanth.ms@firminiq.com
Latest version: 1.1.2

A lightweight, serverless-friendly TypeScript library for publishing backend user activity events from any Lambda function with zero manual initialization and consistent schema, hashing, batching, and retries.

It is fully optimized for AWS serverless environments and integrates seamlessly with a centralized analytics pipeline such as:

Lambdas → SQS → Aggregator Lambda → S3 (ndjson/parquet) → Athena/Glue/QuickSight

✨ Features

Auto-initialization — no manual init() needed. Reads config from environment variables automatically.

Built-in SQS batching (SendMessageBatch, up to 10 per request).

HMAC-SHA256 hashing of PII (e.g., userId → userHash) using secret from Secrets Manager or environment.

Retry with exponential backoff for transient SQS errors.

Minimal dependencies — AWS SDK v3 only.

Extendable schema — send any custom fields; library adds timestamps, eventId, schema version.

Optional validation — plug in AJV or custom validator.

Optional handler wrapper to automatically publish events for each Lambda invocation.

Safe-by-default: no raw PII stored; only hashed identifiers preserved.

📦 Installation
npm install npm i @myvitalrx/activity-publisher
# or
yarn add npm i @myvitalrx/activity-publisher

🔧 Environment Variables (Auto-Init)

The library initializes itself on first use by reading these environment variables:

Variable	Required	Description
EVENT_QUEUE_URL	✔️ Yes	SQS queue URL where events should be published
AWS_REGION	Optional	AWS region (fallback: Lambda runtime default)
EVENT_HASH_SECRET	Optional	HMAC secret for hashing PII (inline)
EVENT_HASH_SECRET_ARN	Optional	Secrets Manager ARN/name containing hashSecret
EVENT_ENABLE_VALIDATION	Optional	"true" or "false"
EVENT_MAX_RETRIES	Optional	Number of SQS send retries (default 3)

Recommended for production:
Use EVENT_HASH_SECRET_ARN to store the hashing secret securely in AWS Secrets Manager.

🚀 Quick Usage
1. Publish a single event (auto-init)
import { publish } from "npm i @myvitalrx/activity-publisher";

export const handler = async (event) => {
  await publish({
    eventType: "VIEW_MEDICAL_RECORD",
    actor: "PROVIDER",
    resourceType: "MEDICAL_RECORD",
    resourceId: event.pathParameters?.id,
    userId: event.requestContext?.authorizer?.principalId, // will be hashed
  });

  return { statusCode: 200, body: "ok" };
};


No init() call required — the library initializes automatically using environment variables.

📚 Sending Multiple Events
import { publishBatch } from "npm i @myvitalrx/activity-publisher";

await publishBatch([
  { eventType: "LOGIN", actor: "PATIENT", userId: "u123" },
  { eventType: "VIEW_DASHBOARD", actor: "PATIENT", userId: "u123" }
]);


The library automatically:

adds timestamps

generates event IDs

hashes userId → userHash

chunks into batches of 10

retries transient failures

🔄 Auto-Publish with wrapHandler

Automatically publish an event every time your Lambda executes:

import { wrapHandler } from "npm i @myvitalrx/activity-publisher";

export const handler = wrapHandler(async (event) => {
  // main logic
  return { statusCode: 200, body: "ok" };
}, {
  mapToEvent: ({ event, res }) => ({
    eventType: "REQUEST_FINISHED",
    actor: "PATIENT",
    resourceType: "API",
    resourceId: event.path,
    userId: event.requestContext?.authorizer?.principalId,
    meta: { statusCode: res?.statusCode },
  })
});


The handler runs normally and the event is published asynchronously.

Hashing & PII Safety

If you pass userId or userIdRaw, the library automatically converts it to:

{
  "userHash": "hmacsha256(...)" 
}


Raw PII is removed from the event before sending.

Secrets can be configured via:

Option A: Inline env (fastest cold starts)
EVENT_HASH_SECRET="mysecret"

Option B: Secrets Manager (recommended for prod)
EVENT_HASH_SECRET_ARN="arn:aws:secretsmanager:..."


Your Lambda execution role must have:

{
  "Effect": "Allow",
  "Action": "secretsmanager:GetSecretValue",
  "Resource": "arn:aws:secretsmanager:*:*:secret:your-secret-id"
}

🧪 Optional Explicit Initialization

You normally don’t need this, but for tests or overrides:

import { init } from "npm i @myvitalrx/activity-publisher";

init({
  queueUrl: "https://sqs.ap-south-1.amazonaws.com/1234567890/dev-events",
  region: "ap-south-1",
  hashSecret: "local-secret"
});

📊 Metrics Hook

You can override metrics collection:

init({
  queueUrl: "...",
  onMetric: (name, value, meta) => {
    console.log("metric", { name, value, ...meta });
  }
});


Events include:

sqs.batch_sent

sqs.batch_send_error

publisher.publish_error

publisher.init_error

🧱 Event Schema (suggested)

You control the structure, but the library adds sensible defaults:

{
  "eventId": "ev-abc123",
  "ts": "2025-11-18T12:00:00Z",
  "schema": "activity.v1",
  "eventType": "VIEW_MEDICAL_RECORD",
  "actor": "PROVIDER",
  "resourceType": "MEDICAL_RECORD",
  "resourceId": "rid-hash",
  "userHash": "abcd1234",
  "meta": { "...": "..." }
}

🛠️ Error Handling

Transient SQS failures → retried automatically (exponential backoff).

Permanent failure → exception thrown (unless using wrapHandler, which swallows errors to avoid disrupting main Lambda).

Missing config → clear error advising required environment variables.

 Testing

For unit tests:

process.env.EVENT_QUEUE_URL = "dummy";
process.env.EVENT_HASH_SECRET = "test";

import { publish, forceInitFromEnv } from "npm i @myvitalrx/activity-publisher";

await forceInitFromEnv(); // reload env


Mock SQS using AWS SDK v3 mocks or intercept calls in Jest.

Architecture Fit

This library works best in:

Microservices using AWS Lambda

Serverless pipelines needing event logs

Healthcare + compliance workloads (PII hashing built-in)

Systems bridging Lambdas → SQS → S3/Athena

📝 License

Internal Use Only — © @myvitalRX