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

@coderule/clients

v2.1.0

Published

TypeScript HTTP clients for core Coderule microservices (Auth, Sync, Retrieval, AST)

Downloads

100

Readme

Coderule TypeScript Client SDK

TypeScript HTTP clients for core Coderule microservices (Auth, Sync, Retrieval, AST) with automatic fetch polyfill support.

Requirements

  • Node.js 14+ (automatically uses node-fetch for versions < 18, native fetch for 18+)
  • TypeScript 5.0+

Node.js Version Compatibility

  • Node.js 18+: Uses native fetch API (no additional dependencies needed)
  • Node.js 14-17: Requires node-fetch to be installed:
    npm install node-fetch@2

The library automatically detects your Node.js version and uses the appropriate fetch implementation.

Installation

npm install
npm run build

Usage Examples

Unified client access

import { CoderuleClients, consoleLogger } from '@coderule/clients';

const clients = new CoderuleClients({
  token: process.env.CODERULE_TOKEN!,
  auth: { baseUrl: 'https://r.coderule.ai:16803' }, // optional, defaults to this host
  logger: consoleLogger, // optional, defaults to nullLogger (silent)
});

const snapshotStatus = await clients.sync.checkSnapshotStatus(snapshotHash);
const retrieval = await clients.retrieval.query(snapshotHash, 'Find the entrypoint');
const visitorRules = await clients.ast.getVisitorRulesV2();

clients.close();

The unified client automatically exchanges your long-lived token for short-lived JWTs using the Auth service. Tokens are cached and refreshed halfway through their lifetime. Whenever the Auth service returns a JWT with a different server_url, the AST, Retrieval, and Sync clients update their base URLs on the fly.

You can override individual service hosts or timeouts:

const clients = new CoderuleClients({
  token,
  auth: { baseUrl: 'https://internal-auth.example.com' },
  retrieval: { timeout: 90_000 },
  sync: { baseUrl: 'http://localhost:8002' }, // static override keeps sync on localhost
});

Manual wiring

If you prefer to manage dependency injection yourself, wire the Auth client and JWT factory and pass the provider into the other clients.

import {
  AuthHttpClient,
  JWTFactory,
  RetrievalHttpClient,
  SyncHttpClient,
  ASTHttpClient,
} from '@coderule/clients';

const authClient = new AuthHttpClient('http://localhost:8001');
const jwtFactory = new JWTFactory(authClient, longLivedToken);

const syncClient = new SyncHttpClient({
  baseUrl: 'http://localhost:8002',
  jwtProvider: jwtFactory,
});

const astClient = new ASTHttpClient({
  baseUrl: 'http://localhost:8003',
  jwtProvider: jwtFactory,
});

const retrievalClient = new RetrievalHttpClient({
  baseUrl: 'http://localhost:8004',
  jwtProvider: jwtFactory,
});

const status = await syncClient.checkSnapshotStatus(snapshotHash);
const queryResult = await retrievalClient.query(snapshotHash, 'Find the entrypoint');
const rules = await astClient.getVisitorRulesV2();

Authentication service

const authClient = new AuthHttpClient('http://localhost:8001');

try {
  const authResponse = await authClient.authenticate('your-token');
  console.log(`JWT expires at: ${authResponse.expires_at}`);

  const health = await authClient.health();
  console.log(`Auth service status: ${health.status}`);
} finally {
  authClient.close();
}

Sync service

const syncClient = new SyncHttpClient({
  baseUrl: 'http://localhost:8002',
  jwtProvider: jwtFactory,
});

const files = [
  { file_path: 'src/main.ts', content: 'const x = 1;' },
  { file_path: 'src/utils.ts', content: 'export function util() {}' },
  { file_path: 'package.json', content: '{"name": "test"}' },
];

const filesInfo = [];
const fileHashes = [];
const fileContents = new Map();

for (const file of files) {
  const fileHash = SyncHttpClient.calculateFileHash(file.file_path, file.content);
  filesInfo.push({ file_path: file.file_path, file_hash: fileHash });
  fileContents.set(fileHash, {
    path: file.file_path,
    content: Buffer.from(file.content),
  });
  fileHashes.push(fileHash);
}

const snapshotHash = SyncHttpClient.calculateSnapshotHash(fileHashes);

const status = await syncClient.checkSnapshotStatus(snapshotHash);
if (status.status === 'NOT_FOUND') {
  const result = await syncClient.createSnapshot(snapshotHash, filesInfo);

  if (result.status === 'MISSING_CONTENT' && result.missing_files) {
    const missingContent = new Map();
    for (const missingFile of result.missing_files) {
      if (fileContents.has(missingFile.file_hash)) {
        missingContent.set(
          missingFile.file_hash,
          fileContents.get(missingFile.file_hash)!,
        );
      }
    }

    if (missingContent.size > 0) {
      await syncClient.uploadFileContent(missingContent);
      await syncClient.createSnapshot(snapshotHash, filesInfo);
    }
  }
}

AST service

const astClient = new ASTHttpClient({
  baseUrl: 'http://localhost:8003',
  jwtProvider: jwtFactory,
});

const health = await astClient.health();
console.log(`AST service status: ${health.status}`);

const rules = await astClient.getVisitorRulesV2();
console.log(`Rules format: ${rules.format}`);

const compiled = ASTHttpClient.compileRulesV2(rules);
const ignoredPredicate = ASTHttpClient.buildIgnoredPredicate(compiled);

Retrieval service

const retrievalClient = new RetrievalHttpClient({
  baseUrl: 'http://localhost:8004',
  jwtProvider: jwtFactory,
});

const result = await retrievalClient.query(
  snapshotHash,
  'Find the main authentication logic',
  3000,
  {
    formatter: 'compact',
    flow_strength: 1.5,
    blend_alpha: 0.8,
  },
);

console.log(result.formatted_output);

const status = await retrievalClient.checkSnapshotStatus(snapshotHash);
console.log(`Snapshot indexing status: ${status.status}`);

const cacheStats = await retrievalClient.getCacheStats();
console.log(`Cached snapshots: ${cacheStats.cached_snapshots}`);

Utility Methods

The SyncHttpClient provides static utility methods for hash calculation:

import { SyncHttpClient } from '@coderule/clients';

// Calculate file hash from path and content
const fileHash = SyncHttpClient.calculateFileHash('src/main.ts', 'const x = 1;');
console.log(`File hash: ${fileHash}`);

// Calculate snapshot hash from file hashes
const fileHashes = ['hash1', 'hash2', 'hash3'];
const snapshotHash = SyncHttpClient.calculateSnapshotHash(fileHashes);
console.log(`Snapshot hash: ${snapshotHash}`);

Error Handling

All clients throw errors with descriptive messages on failures:

try {
  const result = await client.someMethod();
} catch (error) {
  if (error.message.includes('timeout')) {
    console.error('Request timed out');
  } else if (error.message.includes('401')) {
    console.error('Authentication failed');
  } else {
    console.error('Unexpected error:', error.message);
  }
}

Configuration

All clients support timeout configuration in milliseconds:

// Custom timeout of 30 seconds
const retrievalClient = new RetrievalHttpClient({
  baseUrl: 'http://localhost:8004',
  timeout: 30_000,
  jwtProvider: jwtFactory,
  logger: consoleLogger, // optional
});

CoderuleClients also accepts per-service overrides via the auth, ast, retrieval, and sync options. JWTFactory exposes an onTokenRefreshed callback if you need to observe token rotations (for example, to log the current server_url).

Logging

The SDK includes a flexible logging interface that allows you to integrate with any logging library:

import { Logger, nullLogger, consoleLogger } from '@coderule/clients';

// Use the built-in silent logger (default)
const silentClients = new CoderuleClients({
  token,
  logger: nullLogger, // or omit for default
});

// Use the built-in console logger for debugging
const debugClients = new CoderuleClients({
  token,
  logger: consoleLogger,
});

// Implement your own logger
const customLogger: Logger = {
  error: (message, ...meta) => myLogger.error(message, meta),
  warn: (message, ...meta) => myLogger.warn(message, meta),
  info: (message, ...meta) => myLogger.info(message, meta),
  debug: (message, ...meta) => myLogger.debug(message, meta),
};

const clients = new CoderuleClients({
  token,
  logger: customLogger,
});

All individual clients also accept an optional logger parameter:

const authClient = new AuthHttpClient('http://localhost:8001', 30000, consoleLogger);

Breaking Changes

v1.6.0

  • Logger interface added: All clients now accept an optional logger parameter
  • Default behavior is silent: By default, clients use nullLogger which produces no output
  • Backward compatible: The logger parameter is optional, existing code continues to work

v1.4.0

  • Clients now require a JWT provider: SyncHttpClient, RetrievalHttpClient, and ASTHttpClient no longer accept JWT strings per method. Inject a JWTFactory or any JWTProvider implementation through the constructor instead.
  • Unified entry point: CoderuleClients centralises token exchange. Auth defaults to https://r.coderule.ai:16803, and other clients automatically adopt the server_url provided by the Auth service unless explicitly overridden.

Notes

  • Automatic fetch detection: Works with Node.js 14+ (uses node-fetch for < 18, native fetch for 18+)
  • Optional dependency: node-fetch is only required for Node.js versions < 18
  • Zero dependencies for Node.js 18+: Uses native fetch API
  • Clients automatically handle JSON serialization/deserialization
  • JWT handling is centralised through a JWTProvider (JWTFactory or CoderuleClients)
  • AST, Retrieval, and Sync clients update their base URLs when the JWT refresh returns a different server_url (unless a manual override is configured)
  • Health check endpoints don't require authentication
  • All clients have a close() method for consistency, though fetch doesn't maintain persistent connections

Building from Source

# Install dependencies
npm install

# Build TypeScript to JavaScript
npm run build

# Output will be in dist/ directory

License

ISC