npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@gpe-sistemas/tripero-node

v0.6.0

Published

Node.js/TypeScript SDK for Tripero GPS trip detection service

Readme


📋 Overview

tripero-node is the official Node.js/TypeScript SDK for Tripero, an intelligent GPS trip detection and stop analysis microservice. This SDK simplifies integration with Tripero by providing:

  • Type-safe API - Full TypeScript support with complete type definitions
  • Redis PubSub abstraction - Simple methods for publishing positions and subscribing to events
  • HTTP client - Access Tripero's REST API for queries and configuration
  • NestJS integration - Dedicated module with dependency injection support
  • Fire-and-forget - Non-blocking async operations by default

✨ Features

| Feature | Description | |---------|-------------| | 🔌 Easy Integration | Simple API to publish GPS positions and receive trip events | | 📡 Real-time Events | Subscribe to trip starts, completions, stops, and state changes | | 🔒 Type Safety | Full TypeScript definitions for all events and configurations | | ⚡ High Performance | Fire-and-forget publishing with Redis pipelines for batch operations | | 🏗️ NestJS Ready | First-class NestJS support with module, service, and decorators | | 🔧 Configurable | Flexible configuration with sensible defaults | | 📊 HTTP API Access | Query tracker status, trips, stops, and configure odometers | | 🏷️ Metadata Support | Multi-tenancy support with custom metadata propagation |

📦 Installation

# npm
npm install @gpe-sistemas/tripero-node

# yarn
yarn add @gpe-sistemas/tripero-node

# pnpm
pnpm add @gpe-sistemas/tripero-node

Peer Dependencies

For NestJS integration (optional):

npm install @nestjs/common @nestjs/core

🚀 Quick Start

Basic Usage (Node.js)

import { TriperoClient } from '@gpe-sistemas/tripero-node';

// Create client
const tripero = new TriperoClient({
  redis: {
    host: 'localhost',
    port: 6379,
  },
});

// Connect to Redis
await tripero.connect();

// Publish a GPS position
await tripero.publishPosition({
  deviceId: 'VEHICLE-001',
  timestamp: Date.now(),
  latitude: -34.6037,
  longitude: -58.3816,
  speed: 45,
  ignition: true,
});

// Subscribe to trip events
tripero.on('trip:started', (event) => {
  console.log(`🚗 Trip started: ${event.tripId}`);
});

tripero.on('trip:completed', (event) => {
  console.log(`🏁 Trip completed: ${event.tripId}`);
  console.log(`   Distance: ${event.distance}m`);
  console.log(`   Duration: ${event.duration}s`);
});

await tripero.subscribe();

// Cleanup on shutdown
process.on('SIGINT', async () => {
  await tripero.disconnect();
  process.exit(0);
});

With HTTP API

const tripero = new TriperoClient({
  redis: {
    host: 'redis-tripero-service',
    port: 6379,
  },
  http: {
    baseUrl: 'http://tripero-service:3001',
  },
});

await tripero.connect();

// Get tracker status
const status = await tripero.getTrackerStatus('VEHICLE-001');
console.log(`State: ${status.data.currentState.state}`);
console.log(`Odometer: ${status.data.odometer.totalKm} km`);

// Get trip history
const trips = await tripero.getTrips({
  deviceId: 'VEHICLE-001',
  from: new Date('2024-01-01'),
  to: new Date(),
});

// Set odometer (sync with vehicle's real odometer)
await tripero.setOdometer('VEHICLE-001', 125000000, 'vehicle_sync');

📚 API Reference

TriperoClient

The main class for interacting with Tripero.

Constructor

new TriperoClient(options: TriperoClientOptions)

Configuration Options

interface TriperoClientOptions {
  redis: {
    host?: string;           // Default: 'localhost'
    port?: number;           // Default: 6379
    db?: number;             // Default: 0
    password?: string;       // Optional
    username?: string;       // Optional (Redis 6+ ACL)
    keyPrefix?: string;      // Default: 'tripero:'
  };
  http?: {
    baseUrl: string;         // Tripero HTTP API URL
    timeout?: number;        // Default: 10000ms
    headers?: Record<string, string>;
  };
  options?: {
    enableRetry?: boolean;        // Default: false
    enableOfflineQueue?: boolean; // Default: false
    throwOnError?: boolean;       // Default: false
    logLevel?: 'debug' | 'info' | 'warn' | 'error' | 'silent';
  };
}

Connection Methods

| Method | Description | |--------|-------------| | connect() | Connect to Redis server | | disconnect() | Disconnect from Redis server | | health() | Check Redis connection health | | healthHttp() | Check Tripero HTTP API health (requires http config) |

Publishing Methods

publishPosition(position: PositionEvent): Promise<void>

Publish a GPS position to Tripero.

await tripero.publishPosition({
  deviceId: 'VEHICLE-001',       // Required: unique device identifier
  timestamp: Date.now(),          // Required: Unix timestamp in ms
  latitude: -34.6037,             // Required: -90 to 90
  longitude: -58.3816,            // Required: -180 to 180
  speed: 45,                      // Required: km/h
  ignition: true,                 // Optional: engine state
  heading: 180,                   // Optional: degrees 0-360
  altitude: 25,                   // Optional: meters
  accuracy: 5,                    // Optional: GPS accuracy in meters
  satellites: 12,                 // Optional: number of satellites
  metadata: {                     // Optional: custom data
    tenant_id: 'acme-corp',
    fleet_id: 'delivery',
    driver_id: 'driver-123',
  },
});

publishPositions(positions: PositionEvent[]): Promise<void>

Publish multiple positions in a single batch (uses Redis pipeline for performance).

await tripero.publishPositions([
  { deviceId: 'V-001', timestamp: Date.now(), latitude: -34.60, longitude: -58.38, speed: 45 },
  { deviceId: 'V-002', timestamp: Date.now(), latitude: -34.61, longitude: -58.39, speed: 30 },
  { deviceId: 'V-003', timestamp: Date.now(), latitude: -34.62, longitude: -58.40, speed: 55 },
]);

publishIgnitionEvent(event: IgnitionEvent): Promise<void>

Publish an ignition state change.

await tripero.publishIgnitionEvent({
  deviceId: 'VEHICLE-001',
  timestamp: Date.now(),
  ignition: true,  // true = ON, false = OFF
  latitude: -34.6037,
  longitude: -58.3816,
});

Subscription Methods

on(eventType, handler): void

Register an event handler.

tripero.on('trip:started', (event: TripStartedEvent) => {
  console.log(`Trip ${event.tripId} started at ${event.startTime}`);
});

tripero.on('trip:completed', (event: TripCompletedEvent) => {
  console.log(`Trip ${event.tripId}: ${event.distance}m in ${event.duration}s`);
});

tripero.on('stop:started', (event: StopStartedEvent) => {
  console.log(`Stop detected: ${event.reason}`);
});

tripero.on('tracker:state:changed', (event: TrackerStateChangedEvent) => {
  console.log(`${event.previousState} → ${event.currentState}`);
});

off(eventType, handler): void

Remove an event handler.

subscribe(): Promise<void>

Start listening to all registered events.

unsubscribe(): Promise<void>

Stop listening to events.

HTTP API Methods

Note: These methods require the http configuration option.

getTrackerStatus(trackerId: string): Promise<TrackerStatusResponse>

Get real-time status of a tracker including state, odometer, position, and statistics.

setOdometer(trackerId: string, meters: number, reason?: string): Promise<OdometerSetResponse>

Set the odometer offset to sync with the vehicle's real odometer.

getTrips(options: ReportQueryOptions): Promise<TripReport[]>

Query historical trips.

const trips = await tripero.getTrips({
  deviceId: 'VEHICLE-001',           // or ['V-001', 'V-002'] or 'all'
  from: new Date('2024-01-01'),
  to: new Date(),
  tenantId: 'acme-corp',             // Optional: filter by tenant
  fleetId: 'delivery',               // Optional: filter by fleet
});

getStops(options: ReportQueryOptions): Promise<StopReport[]>

Query historical stops with the same options as getTrips().

🏗️ NestJS Integration

Module Setup

// app.module.ts
import { Module } from '@nestjs/common';
import { TriperoModule } from '@gpe-sistemas/tripero-node/nestjs';

@Module({
  imports: [
    // Static configuration
    TriperoModule.forRoot({
      redis: {
        host: 'redis-tripero-service',
        port: 6379,
      },
      http: {
        baseUrl: 'http://tripero-service:3001',
      },
    }),
  ],
})
export class AppModule {}

Async Configuration

// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TriperoModule } from '@gpe-sistemas/tripero-node/nestjs';

@Module({
  imports: [
    ConfigModule.forRoot(),
    TriperoModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        redis: {
          host: config.get('TRIPERO_REDIS_HOST', 'localhost'),
          port: config.get('TRIPERO_REDIS_PORT', 6379),
          keyPrefix: config.get('TRIPERO_KEY_PREFIX', 'tripero:'),
        },
        http: {
          baseUrl: config.get('TRIPERO_HTTP_URL'),
        },
      }),
    }),
  ],
})
export class AppModule {}

Using the Service

// position.service.ts
import { Injectable } from '@nestjs/common';
import { TriperoService } from '@gpe-sistemas/tripero-node/nestjs';

@Injectable()
export class PositionService {
  constructor(private readonly tripero: TriperoService) {}

  async handleGpsData(data: GpsData) {
    await this.tripero.publishPosition({
      deviceId: data.imei,
      timestamp: Date.now(),
      latitude: data.lat,
      longitude: data.lon,
      speed: data.speed,
      ignition: data.acc,
      metadata: {
        tenant_id: data.tenantId,
      },
    });
  }

  async getVehicleStatus(deviceId: string) {
    return this.tripero.getTrackerStatus(deviceId);
  }
}

Event Decorators

// trip-listener.service.ts
import { Injectable, OnModuleInit } from '@nestjs/common';
import { TriperoService } from '@gpe-sistemas/tripero-node/nestjs';
import type { TripStartedEvent, TripCompletedEvent } from '@gpe-sistemas/tripero-node';

@Injectable()
export class TripListenerService implements OnModuleInit {
  constructor(private readonly tripero: TriperoService) {}

  async onModuleInit() {
    // Register event handlers
    this.tripero.on('trip:started', this.handleTripStarted.bind(this));
    this.tripero.on('trip:completed', this.handleTripCompleted.bind(this));

    // Start listening
    await this.tripero.subscribe();
  }

  private handleTripStarted(event: TripStartedEvent) {
    console.log(`🚗 Trip started: ${event.tripId}`);
    // Send notification, update dashboard, etc.
  }

  private handleTripCompleted(event: TripCompletedEvent) {
    console.log(`🏁 Trip completed: ${event.tripId}`);
    console.log(`   Distance: ${event.distance}m`);
    console.log(`   Duration: ${event.duration}s`);
    // Generate report, calculate fuel, etc.
  }
}

📡 Events

Input Events (publish to Tripero)

| Channel | Method | Description | |---------|--------|-------------| | position:new | publishPosition() | GPS position data | | ignition:changed | publishIgnitionEvent() | Ignition state changes |

Output Events (receive from Tripero)

| Event | Description | Key Fields | |-------|-------------|------------| | tracker:state:changed | Tracker state transition | previousState, currentState, odometer | | trip:started | Trip has started | tripId, deviceId, startTime, startLocation | | trip:completed | Trip has ended | tripId, distance, duration, avgSpeed, maxSpeed | | stop:started | Stop detected during trip | stopId, tripId, location, reason | | stop:completed | Stop has ended | stopId, duration | | position:rejected | Position failed validation | deviceId, reason |

Tracker States

STOPPED ←→ IDLE ←→ MOVING

| State | Description | |-------|-------------| | STOPPED | Vehicle completely stopped (ignition OFF) | | IDLE | Engine running but not moving (ignition ON, speed ≈ 0) | | MOVING | Vehicle in motion (ignition ON, speed > threshold) |

🏷️ Metadata Support

Tripero supports custom metadata that propagates automatically from positions to trips and stops:

await tripero.publishPosition({
  deviceId: 'VEHICLE-001',
  timestamp: Date.now(),
  latitude: -34.6037,
  longitude: -58.3816,
  speed: 45,
  metadata: {
    // Optimized fields (B-tree index, ~1-2ms queries)
    tenant_id: 'acme-corp',
    client_id: 'client-123',
    fleet_id: 'delivery-trucks',

    // Custom fields (GIN index, ~5-10ms queries)
    driver_id: 'driver-456',
    route_number: 'R42',
    delivery_id: 'DEL-789',
  },
});

Query trips/stops by metadata:

const trips = await tripero.getTrips({
  deviceId: 'all',
  from: new Date('2024-01-01'),
  to: new Date(),
  tenantId: 'acme-corp',      // Fast query (~1-2ms)
  fleetId: 'delivery-trucks', // Fast query (~1-2ms)
});

⚙️ Configuration

Environment Variables

When using with NestJS ConfigService:

| Variable | Description | Default | |----------|-------------|---------| | TRIPERO_REDIS_HOST | Redis server host | localhost | | TRIPERO_REDIS_PORT | Redis server port | 6379 | | TRIPERO_REDIS_DB | Redis database number | 0 | | TRIPERO_REDIS_PASSWORD | Redis password | - | | TRIPERO_KEY_PREFIX | Redis key/channel prefix | tripero: | | TRIPERO_HTTP_URL | Tripero HTTP API URL | - | | TRIPERO_LOG_LEVEL | Log level | info |

Redis Key Prefix

The SDK uses tripero: as the default key prefix to match Tripero server defaults. This allows sharing a Redis instance with other applications:

const tripero = new TriperoClient({
  redis: {
    host: 'shared-redis',
    keyPrefix: 'myapp:tripero:', // Custom prefix
  },
});

Important: The keyPrefix must match the REDIS_KEY_PREFIX configured in Tripero server.

🔧 Advanced Usage

Custom Logger

import { TriperoClient, TriperoLogger } from '@gpe-sistemas/tripero-node';

const customLogger: TriperoLogger = {
  debug: (msg, ...args) => myLogger.debug(msg, ...args),
  info: (msg, ...args) => myLogger.info(msg, ...args),
  warn: (msg, ...args) => myLogger.warn(msg, ...args),
  error: (msg, ...args) => myLogger.error(msg, ...args),
};

const tripero = new TriperoClient({
  redis: { host: 'localhost' },
  options: {
    logger: customLogger,
  },
});

Error Handling

By default, the SDK logs errors but doesn't throw (fire-and-forget). To enable exceptions:

const tripero = new TriperoClient({
  redis: { host: 'localhost' },
  options: {
    throwOnError: true,
  },
});

try {
  await tripero.publishPosition(position);
} catch (error) {
  console.error('Failed to publish:', error);
}

Connection Retry

const tripero = new TriperoClient({
  redis: {
    host: 'localhost',
    redisOptions: {
      retryStrategy: (times) => Math.min(times * 1000, 30000),
      maxRetriesPerRequest: 3,
    },
  },
  options: {
    enableRetry: true,
    enableOfflineQueue: true,
  },
});

🤝 Contributing

Contributions are welcome! Here's how you can help:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Development Setup

# Clone the repository
git clone https://github.com/GPE-Sistemas/tripero-node.git
cd tripero-node

# Install dependencies
npm install

# Build
npm run build

# Run tests
npm test

# Lint
npm run lint

Code Style

  • Follow TypeScript best practices
  • Use meaningful variable and function names
  • Add JSDoc comments for public APIs
  • Write tests for new features

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

MIT License

Copyright (c) 2024 GPE Sistemas

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

🔗 Related Projects

  • Tripero - GPS trip detection microservice
  • IRIX - Fleet management platform

📞 Support