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

@glowlabs-org/events-sdk

v1.0.20

Published

Typed event SDK for Glow, powered by RabbitMQ and Zod.

Readme

Glow Events SDK

A TypeScript-first SDK for consuming and emitting typed events on the Glow platform, powered by RabbitMQ (topic exchange). Provides runtime validation and type inference using Zod schemas.


🚀 Quick Start

1. Install

pnpm install @glowlabs-org/events-sdk

📚 Zones

The following zones are currently available:

| Zone ID | Zone Name | | ------- | ------------------ | | 0 | All Zones | | 1 | Clean Grid Project | | 2 | Coming Soon Zone |

  • Use zoneId: 0 (with zoneName: "All Zones") to listen to all zones. Emitters must be constructed with a specific zoneId (not 0).

  • The SDK types and runtime validation now officially support zoneId: 0 and zoneName: "All Zones" everywhere for listeners, but not for emitters.

  • Use the getZoneId utility to get the correct zoneId for a given zone name:

    import { getZoneId } from "@glowlabs-org/events-sdk";
    const zoneId = getZoneId("CleanGridProject");

🛠️ Options for Listeners & Emitters

Both createGlowEventListener and createGlowEventEmitter accept the following options:

| Option | Type | Required | Description | | ---------------- | ------ | -------- | ------------------------------------------------------------------ | | username | string | Yes | RabbitMQ username | | password | string | Yes | RabbitMQ password | | zoneId | number | Yes | Zone ID (use getZoneId("ZoneName")) | | queueName | string | No | (Listener only) Pre-created queue name | | exchangePrefix | string | No | Exchange prefix (default: glow.zone-) | | host | string | No | RabbitMQ host/port (default: turntable.proxy.rlwy.net:50784) |


📦 Event Types & Versions

Currently supported event types and versions:

| Event Name | Version | Payload Type | Description | | ------------------------- | ---------- | ------------------------------------- | --------------------------------------------------- | | audit.pushed | "v1" | AuditPushedV1Payload | Emitted when an audit is pushed | | audit.slashed | "v1" | AuditSlashedV1Payload | Emitted when a farm is slashed | | audit.pfees.paid | "v1" | AuditPfeesPaidV1Payload | Paid (by applicationId) | | audit.pfees.paid | "v2" | AuditPfeesPaidV2Payload | Paid (by farmId) | | audit.pfees.paid | "v2-alpha" | AuditPfeesPaidV2AlphaPayload | Paid (by applicationId) + currency metadata | | application.created | "v1" | ApplicationCreatedV1Payload | Emitted when an application is created | | application.created | "v2-alpha" | ApplicationCreatedV2AlphaPayload | V2-alpha: adds protocol fee price, omits installer | | audit.pushed | "v2" | AuditPushedV2Payload | Emitted when an audit is pushed (farmId is bytes16) | | audit.pushed | "v2-alpha" | AuditPushedV2AlphaPayload | Same as v1 but without protocol fee | | application.price.quote | "v2-alpha" | ApplicationPriceQuoteV2AlphaPayload | Price quote for application | | gctl.minted | "v2-alpha" | GctlMintedV2AlphaPayload | GCTL minted amount and metadata | | auditor.fees.paid | "v2-alpha" | AuditorFeesPaidV2AlphaPayload | Auditor fee paid (by applicationId) | | fraction.created | "v2-alpha" | FractionCreatedV2AlphaPayload | Fraction created with token and ownership details | | fraction.sold | "v2-alpha" | FractionSoldV2AlphaPayload | Fraction sold between creator and buyer | | fraction.closed | "v2-alpha" | FractionClosedV2AlphaPayload | Fraction closed/cancelled | | fraction.refunded | "v2-alpha" | FractionRefundedV2AlphaPayload | Fraction refunded to user |

Environment: All events include an environment field that defaults to "production". You can set it to "staging" when creating an emitter.


Event Types Enum

The SDK provides a TypeScript constant for all supported event types to ensure type safety and avoid typos:

import { eventTypes, EventType } from "./src/event-types";

// Usage example:
const myEventType: EventType = eventTypes.auditPushed;
  • eventTypes is a readonly object containing all event type strings.
  • EventType is a TypeScript type representing any valid event type string.

Use these in your code to avoid hardcoding event names and to benefit from autocompletion and type checking.


📝 Event Payload Schemas

audit.pushed v1

export interface AuditPushedV1Payload {
  farmId: string; // UUID string
  protocolFeeUSDPrice_6Decimals: string; // uint256 (decimal) − 12 implied decimals
  expectedProduction_12Decimals: string; // uint256 (decimal) − 12 implied decimals
  txHash: string; // bytes32 hex string (0x...)
}

Validation:

  • farmId must be a valid UUID string (e.g., afbc56b6-0b16-4119-b144-025728067ba6).
  • protocolFeeUSDPrice_6Decimals and expectedProduction_12Decimals must be decimal strings representing unsigned big integers.
  • txHash must be a 32-byte hex string (e.g., 0x...).

audit.slashed v1

export interface AuditSlashedV1Payload {
  farmId: string; // bytes16 hex string (0x...)
  slasher: string; // Ethereum address (0x...)
  txHash: string; // bytes32 hex string (0x...)
}

Validation:

  • farmId must be a 16-byte hex string (e.g., 0x...).
  • slasher must be a valid Ethereum address (0x...40 hex chars).
  • txHash must be a 32-byte hex string (e.g., 0x...).

audit.pfees.paid v1 (by applicationId)

export interface AuditPfeesPaidV1Payload {
  applicationId: string; // UUID string
  payer: string; // Ethereum address (0x...)
  amount_6Decimals: string; // uint256 (decimal) − 6 implied decimals
  txHash: string; // bytes32 hex string (0x...)
}

Validation:

  • applicationId must be a valid UUID string (e.g., 3ed964b1-4f02-475a-9789-fb74b3466c70).
  • payer must be a valid Ethereum address (0x...40 hex chars).
  • amount_6Decimals must be a decimal string representing an unsigned big integer (6 implied decimals).
  • txHash must be a 32-byte hex string (e.g., 0x...).

audit.pfees.paid v2 (by farmId)

export interface AuditPfeesPaidV2Payload {
  farmId: string; // bytes16 hex string (0x...)
  payer: string; // Ethereum address (0x...)
  amount_12Decimals: string; // uint256 (decimal) − 12 implied decimals
  txHash: string; // bytes32 hex string (0x...)
}

Validation:

  • farmId must be a 16-byte hex string (e.g., 0x...).
  • payer must be a valid Ethereum address (0x...40 hex chars).
  • amount_12Decimals must be a decimal string representing an unsigned big integer (12 implied decimals).
  • txHash must be a 32-byte hex string (e.g., 0x...).

audit.pushed v2

export interface AuditPushedV2Payload {
  farmId: string; // bytes16 hex string (0x...)
  protocolFeeUSDPrice_12Decimals: string; // uint256 (decimal) − 12 implied decimals
  expectedProduction_12Decimals: string; // uint256 (decimal) − 12 implied decimals
  txHash: string; // bytes32 hex string (0x...)
}

### `audit.pushed` v2-alpha

```ts
export interface AuditPushedV2AlphaPayload {
  farmId: string; // UUID string
  expectedProduction_12Decimals: string; // uint256 (decimal) − 12 implied decimals
  txHash: string; // bytes32 hex string (0x...)
}

audit.pfees.paid v2-alpha

export type PaymentCurrency = "GCTL" | "USDC" | "USDG" | "GLW" | "SGCTL";
export interface AuditPfeesPaidV2AlphaPayload {
  applicationId: string; // UUID string
  payer: string; // Ethereum address (0x...)
  amount_6Decimals: string; // uint256 (decimal) − 6 implied decimals
  txHash: string; // bytes32 hex string (0x...)
  paymentCurrency: PaymentCurrency;
  paymentEventType: string;
  isSponsored: boolean;
}

application.price.quote v2-alpha

export interface ApplicationPriceQuoteV2AlphaPayload {
  applicationId: string; // UUID string
  createdAt: string; // ISO-8601 datetime string
  prices: { GCTL: string; GLW: string; USDC: string; USDG: string }; // uint256 decimal strings
  signature: string; // bytes32 hex string (0x...)
  gcaAddress: string; // Ethereum address (0x...)
}

gctl.minted v2-alpha

export interface GctlMintedV2AlphaPayload {
  gctlAmount_6Decimals: string; // uint256 (decimal) − 6 implied decimals
  timestamp: string; // ISO-8601 datetime string
  minter: string; // Ethereum address (0x...)
  txHash: string; // bytes32 hex string (0x...)
}

auditor.fees.paid v2-alpha

export interface AuditorFeesPaidV2AlphaPayload {
  applicationId: string; // UUID string
  payer: string; // Ethereum address (0x...)
  amount_6Decimals: string; // uint256 (decimal) − 6 implied decimals
  txHash: string; // bytes32 hex string (0x...)
}

fraction.created v2-alpha

export interface FractionCreatedV2AlphaPayload {
  fractionId: string;
  transactionHash: string; // bytes32 hex string (0x...)
  blockNumber: string; // uint256 (decimal)
  logIndex: number;
  token: string; // Ethereum address (0x...)
  owner: string; // Ethereum address (0x...)
  step: string; // uint256 (decimal)
  totalSteps: string; // uint256 (decimal)
}

fraction.sold v2-alpha

export interface FractionSoldV2AlphaPayload {
  fractionId: string;
  transactionHash: string; // bytes32 hex string (0x...)
  blockNumber: string; // uint256 (decimal)
  logIndex: number;
  creator: string; // Ethereum address (0x...)
  buyer: string; // Ethereum address (0x...)
  step: string; // uint256 (decimal)
  amount: string; // uint256 (decimal)
  timestamp: number;
}

fraction.closed v2-alpha

export interface FractionClosedV2AlphaPayload {
  fractionId: string;
  transactionHash: string; // bytes32 hex string (0x...)
  blockNumber: string; // uint256 (decimal)
  logIndex: number;
  token: string; // Ethereum address (0x...)
  owner: string; // Ethereum address (0x...)
  timestamp: number;
}

Validation:

  • fractionId is a string identifier.
  • transactionHash must be a 32-byte hex string.
  • blockNumber, step, totalSteps, and amount must be decimal strings representing unsigned big integers.
  • token, owner, creator, and buyer must be valid Ethereum addresses.

export interface FractionSoldV2AlphaPayload {
  fractionId: string;
  transactionHash: string; // bytes32 hex string (0x...)
  blockNumber: string; // uint256 (decimal)
  logIndex: number;
  creator: string; // Ethereum address (0x...)
  buyer: string; // Ethereum address (0x...)
  step: string; // uint256 (decimal)
  amount: string; // uint256 (decimal)
  timestamp: number;
}

fraction.closed v2-alpha

export interface FractionClosedV2AlphaPayload {
  fractionId: string;
  transactionHash: string; // bytes32 hex string (0x...)
  blockNumber: string; // uint256 (decimal)
  logIndex: number;
  token: string; // Ethereum address (0x...)
  owner: string; // Ethereum address (0x...)
  timestamp: number;
}

fraction.refunded v2-alpha

export interface FractionRefundedV2AlphaPayload {
  fractionId: string; // hex string
  creator: string; // Ethereum address (0x...)
  user: string; // Ethereum address (0x...)
  refundTo: string; // Ethereum address (0x...)
  amount: string; // uint256 (decimal)
  timestamp: number;
  blockNumber: string; // uint256 (decimal)
  transactionHash: string; // bytes32 hex string (0x...)
  logIndex: number;
}

application.created v2-alpha

export interface ApplicationCreatedV2AlphaPayload {
  gcaAddress: string; // Ethereum address (0x...)
  lat: number;
  lng: number;
  estimatedCostOfPowerPerKWh: number;
  estimatedKWhGeneratedPerYear: number;
  estimatedProtocolFeeUSDPrice_6Decimals: string; // uint256 (decimal) − 6 implied decimals
}

Validation:

  • gcaAddress must be a valid Ethereum address (0x...40 hex chars).
  • lat and lng are numbers (coordinates).
  • estimatedCostOfPowerPerKWh and estimatedKWhGeneratedPerYear are numbers.
  • installerCompanyName is a string.

✨ Usage Example

Listen to Specific Event Types/Versions

import { createGlowEventListener, getZoneId } from "@glowlabs-org/events-sdk";

const listener = createGlowEventListener({
  username: "listener",
  password: "your-password-here",
  zoneId: getZoneId("CleanGridProject"),
  queueName: "my.precreated.queue",
  host: "my.rabbitmq.host:5672", // Optional: override the default host
});

listener.onEvent("audit.pushed", "v1", (event) => {
  // event: GlowEvent<AuditPushedV1Payload>
  console.log(
    "Received audit.pushed v1:",
    event.payload.farmId,
    event.zoneId,
    event.zoneName
  );
});

listener.onEvent("audit.slashed", "v1", (event) => {
  // event: GlowEvent<AuditSlashedV1Payload>
  console.log(
    "Received audit.slashed v1:",
    event.payload.farmId,
    event.payload.slasher
  );
});

listener.onEvent("audit.pfees.paid", "v1", (event) => {
  // event: GlowEvent<AuditPfeesPaidV1Payload>
  console.log(
    "Received audit.pfees.paid v1:",
    event.payload.applicationId,
    event.payload.payer,
    event.payload.amount_6Decimals
  );
});

listener.onEvent("audit.pfees.paid", "v2", (event) => {
  // event: GlowEvent<AuditPfeesPaidV2Payload>
  console.log(
    "Received audit.pfees.paid v2:",
    event.payload.farmId,
    event.payload.payer,
    event.payload.amount_12Decimals
  );
});

listener.onEvent("application.created", "v1", (event) => {
  // event: GlowEvent<ApplicationCreatedV1Payload>
  console.log(
    "Received application.created v1:",
    event.payload.gcaAddress,
    event.payload.lat,
    event.payload.lng,
    event.payload.estimatedCostOfPowerPerKWh,
    event.payload.estimatedKWhGeneratedPerYear,
    event.payload.installerCompanyName
  );
});

await listener.start();
// To stop listening:
// await listener.stop();

Emit Events (Admin Only)

import { createGlowEventEmitter, getZoneId } from "@glowlabs-org/events-sdk";

// You must construct the emitter with a specific zoneId (not 0)
const emitter = createGlowEventEmitter({
  username: "admin",
  password: "your-password-here",
  zoneId: getZoneId("CleanGridProject"), // must be a specific zone
  host: "my.rabbitmq.host:5672", // Optional: override the default host
  environment: "staging", // Optional: defaults to "production"
});

await emitter.emit({
  eventType: "audit.pushed",
  schemaVersion: "v1",
  payload: {
    farmId: "afbc56b6-0b16-4119-b144-025728067ba6", // UUID string
    protocolFeeUSDPrice_6Decimals: "...",
    expectedProduction_12Decimals: "...",
    txHash: "0x...",
  },
});

await emitter.emit({
  eventType: "audit.slashed",
  schemaVersion: "v1",
  payload: {
    farmId: "0x...",
    slasher: "0x...",
    txHash: "0x...",
  },
});

await emitter.emit({
  eventType: "audit.pfees.paid",
  schemaVersion: "v1",
  payload: {
    applicationId: "3ed964b1-4f02-475a-9789-fb74b3466c70", // UUID string
    payer: "0x...",
    amount_6Decimals: "1000000000000",
    txHash: "0x...",
  },
});

await emitter.emit({
  eventType: "audit.pfees.paid",
  schemaVersion: "v2",
  payload: {
    farmId: "0x...",
    payer: "0x...",
    amount_12Decimals: "1000000000000",
    txHash: "0x...",
  },
});

await emitter.emit({
  eventType: "application.created",
  schemaVersion: "v1",
  payload: {
    gcaAddress: "0x...",
    lat: 45.5017,
    lng: -73.5673,
    estimatedCostOfPowerPerKWh: 0.12,
    estimatedKWhGeneratedPerYear: 10000,
    installerCompanyName: "SolarCo",
  },
});

await emitter.disconnect();

Note:

  • The emitter will automatically publish each event to both the global (zone 0) and the specific zone exchange.
  • You cannot construct an emitter for zoneId: 0, and you cannot specify zoneId per emit call.
  • schemaVersion is always a string (e.g., "v1", "v2", "v2-alpha").
  • You can override the RabbitMQ host using the host option. Default is turntable.proxy.rlwy.net:50784.

🌐 Listening to All Zones

You can listen to all zones at once by passing zoneId: 0 and zoneName: "All Zones" to the listener. Emitters must always use a specific zone.

Listen to All Zones

import { createGlowEventListener } from "@glowlabs-org/events-sdk";

const listener = createGlowEventListener({
  username: "listener",
  password: "your-password-here",
  zoneId: 0, // special value for all zones
  queueName: "my.precreated.queue",
});

listener.onEvent("audit.pushed", "v1", (event) => {
  console.log(
    "Received audit.pushed v1 from any zone:",
    event.payload.farmId,
    event.zoneId,
    event.zoneName
  );
});

// ... other event handlers ...

await listener.start();
// To stop listening:
// await listener.stop();

🧪 Validation & Error Handling

  • All events are validated at runtime using Zod schemas.
  • If you emit or process an event with a zoneName that does not match the zoneId, an error is thrown. zoneId: 0 and zoneName: "All Zones" are a valid pairing.
  • If you emit or process an event with a schemaVersion for which no schema exists (e.g., audit.pushed v2), an error is thrown.
  • If the payload does not match the schema, an error is thrown.

🔐 Permissions & Credentials

  • Listener credentials: Can only subscribe to events. Cannot emit events or create new queues.
  • Admin credentials: Can subscribe, emit events, and create/bind new queues and exchanges.

If you try to emit with listener credentials, the SDK will throw an error.


🛠️ Admin & Queue Management

The SDK exposes helpers for programmatically creating, binding, and deleting exchanges and queues (admin credentials required). Use these for pre-creating queues for listeners, bootstrapping environments, or advanced queue management.

createExchange(options)

Creates a topic exchange (default: exchangeType = "topic").

bindQueueToExchange(options)

Binds a queue to a topic exchange. You can specify a routingKey for fine-grained event filtering:

  • routingKey = "#" (default): all events
  • routingKey = "audit.pushed.v1": only audit.pushed v1 events
  • routingKey = "audit.pushed.*": all versions of audit.pushed

Example

import {
  createExchange,
  bindQueueToExchange,
  deleteExchange,
  deleteQueue,
} from "@glowlabs-org/events-sdk";

await createExchange({
  username: "admin",
  password: "your-password-here",
  exchange: "glow.zone-1.events",
});

await bindQueueToExchange({
  username: "admin",
  password: "your-password-here",
  exchange: "glow.zone-1.events",
  queue: "glow-listener-queue",
  routingKey: "audit.pushed.v1", // only audit.pushed v1 events
});

🔒 Strict Read-Only Listeners

If your listener credentials only have read permission (no configure), you must consume from a pre-created queue. This is the most secure pattern for production.

1. Admin: Pre-create and bind the queue

import { bindQueueToExchange } from "@glowlabs-org/events-sdk";

await bindQueueToExchange({
  username: "admin",
  password: "your-admin-password",
  exchange: "glow.zone-1.events",
  queue: "my.precreated.queue",
  routingKey: "audit.pushed.v1", // only audit.pushed v1 events
});

2. Listener: Consume from the pre-created queue

import { createGlowEventListener } from "@glowlabs-org/events-sdk";

const listener = createGlowEventListener({
  username: "listener",
  password: "your-listener-password",
  zoneId: 1,
  queueName: "my.precreated.queue",
});
  • The listener will only consume from the pre-created queue and will not attempt to create or bind anything.
  • This pattern is required for production environments with strict access control.

🧩 Advanced: Multiple Listeners/Emitters

You can create multiple listeners or emitters in the same process, each with its own configuration (e.g., for different credentials, exchanges, or RabbitMQ URLs). This is useful for multi-tenant, multi-topic, or advanced scenarios. Every listener receives every event for the bound routing key(s).


🧪 Extending Event Types

To add new event types or versions:

  1. Create a new schema in src/schemas/.
  2. Add the event type and version to eventTypeRegistry in src/event-registry.ts.
  3. Update the base event type in src/base-event.ts if needed.

🏗️ Build & Publish to npm

To build and publish the SDK to npm:

make build
make publish
make clean
  • The first time, run npm login to authenticate with npm.
  • For scoped packages (like @glowlabs-org/events-sdk), the Makefile uses --access public for publishing.

License

MIT