npm package: @sailfish-ai/sf-veritas
Description: A versatile Edge Runtime-compatible package for JavaScript and TypeScript backend systems and scripts.
Author: SailfishAI
Latest version: 0.2.20

# Sailfish's Backend Data Collector for JavaScript and TypeScript

Sailfish's veritas library captures all of the data to enable engineers to properly debug issues.

## Supported Frameworks

As of September 2025, we support [the most widely-used JS/TS backend frameworks](https://survey.stackoverflow.co/2025/technology#most-popular-technologies-webframe) and others (in both JavaScript and TypeScript):

- Most-widely Used
  - Node.js
  - Next.js
  - Express
  - Angular
  - NestJS
  - NuxtJS
- Others
  - Apollo Server
  - Mercurius
  - MeteorJS
  - GraphQL over HTTP (formerly Express GraphQL)

## Installation

#### Install the Package

Install via your package manager; for reference, here is how you can install our package via `npm` and `yarn`:

```sh title="Install via npm"
npm install --save @sailfish-ai/sf-veritas
```

```sh title="Install via yarn"
yarn add @sailfish-ai/sf-veritas
```

## Integration

Once you have installed the `sf-veritas` package, it's time to integrate it into your project.

### Arguments/Parameters

Below are the configuration parameters for `sf-veritas`. These options are passed as an object to the `setupInterceptors` function.

- **`apiKey`**: `string`

  - This can be seen and copied from your [Settings Configuration page](https://app.sailfishqa.com/settings/configuration) in the "Sailfish Configuration" section.

- **`serviceIdentifier`**: `string`

  - This identifies the service that is running.
  - The value is extremely important because Sailfish uses the `serviceIdentifier` to link sevices to the location in code. Without this, Sailfish can have issues locating code and automatically fixing issues.
    - e.g. **`gitOrg`/`gitRepo`/`pathToFile`**

- **`serviceVersion`**: `string`

  - This identifies the version of your service - this is a human-readable version and is not required.

- **`gitSha`**: `string`

  - This is the Git SHA of your releases - populating this via this argument or an environment variable helps your team tie issues to code changes quickly.

- **`serviceAdditionalMetadata`**: `Record<string, string | number | boolean | null>`

  - Add extra metadata such as cluster information or environment details.

- **`domainsToNotPropagateHeadersTo`**: `string[]`

  - Prevents adding tracing headers (`X-Sf3-Rid`) to certain domains.
    - Supports wildcard characters, subdomains, and paths.

- **`nodeModulesToCollectLocalVariablesOn`**: `string[]` (_coming soon_)
  - Specify packages or modules for capturing local variable values during errors or exceptions.

### Integration

To integrate and start, place the following into your application's entry point, **as early as possible**:

```ts title="JS/TS integration code"
import { setupInterceptors } from "@sailfish-ai/sf-veritas";

setupInterceptors({
  apiKey: "your-api-key",
  serviceIdentifier: "<gitOrg>/<gitRepo>/<pathToFile>",
  domainsToNotPropagateHeadersTo: ["example.com"],
});
```

### Runtime Hooks for Local Development

For local development and debugging without a build step, `sf-veritas` provides runtime hooks that automatically instrument your code on-the-fly.

**⚠️ Important**: Runtime hooks are intended for **development only**. For production, use the build plugins (webpack, vite, rollup, esbuild, or tsc) which provide better performance.

#### Node 20+ with ESM Loader (Recommended)

Use the `--import` flag with the loader registration:

```bash
# With Node.js directly
node --import @sailfish-ai/sf-veritas/runtime/register-loader app.js

# With tsx (TypeScript)
tsx --import @sailfish-ai/sf-veritas/runtime/register-loader app.ts

# In package.json scripts
{
  "scripts": {
    "dev": "node --import @sailfish-ai/sf-veritas/runtime/register-loader src/app.js"
  }
}
```

#### Configuration

Runtime hooks respect the same configuration as build plugins:

**Environment Variables:**
```bash
SF_FUNCSPAN_CONSOLE_OUTPUT=true                 # Output function spans to console (for development)
SF_FUNCSPAN_JSONL_FILE=true                     # Write function spans to funcspans.jsonl (default)
SF_FUNCSPAN_JSONL_FILE=/path/to/spans.jsonl     # Write function spans to custom JSONL file
SF_FUNCSPAN_SKIP_BACKEND=true                   # Skip sending spans to backend (for local-only)
SF_FUNCSPAN_DEBUG=true                          # Enable debug logging
SF_FUNCSPAN_SAMPLE_RATE=1.0                     # Override sampling rate (0.0-1.0)
SF_FUNCSPAN_INCLUDE_NODE_MODULES=express,axios  # Instrument specific packages
```

**Console Output Example:**

When `SF_FUNCSPAN_CONSOLE_OUTPUT=true` is set, you'll see clean output like:

```
📊 Function Span: {
  function: 'add (src/utils.ts:10:0)',
  duration: '0.05ms',
  arguments: { a: 2, b: 3 },
  result: 5,
  spanId: 'a05a6dea-90ac-4c86-9d74-7815d8de87d2',
  parentSpanId: 'none'
}
```

**JSONL File Output:**

When `SF_FUNCSPAN_JSONL_FILE=/path/to/file.jsonl` is set, each function span is written as a JSON line:

```json
{"timestamp":"2025-11-27T18:06:26.821Z","functionName":"add","filePath":"src/utils.ts","line":10,"column":0,"duration":0.05,"arguments":{"a":2,"b":3},"result":5,"spanId":"abc123","parentSpanId":"parent-id","async":false}
```

This format is ideal for log processing tools and can be easily analyzed with `jq`:

```bash
# Pretty print a span
cat spans.jsonl | head -1 | jq .

# Find slow functions
cat spans.jsonl | jq 'select(.duration > 100)'

# Group by function name
cat spans.jsonl | jq -s 'group_by(.functionName) | map({function: .[0].functionName, count: length})'
```

**`.sailfish` Configuration Files:**

Create a `.sailfish` file in your project directory:

```json
{
  "default": {
    "sample_rate": 1.0,
    "capture_arguments": true,
    "capture_return_value": true
  }
}
```

#### Programmatic API

You can also register hooks programmatically:

```typescript
import { registerFuncspanRuntime } from '@sailfish-ai/sf-veritas/runtime/register';

// Must be called before importing any modules you want to instrument
registerFuncspanRuntime({
  debug: true,
  includeNodeModules: ['express'],
  sampleRate: 1.0
});

// Now import your application code
import './app';
```

#### Requirements

- Node.js 16.12+ (stable in Node 18+, recommended: Node 20+)
- For ESM loader: Node 20.6+ for full support

#### Limitations

- Runtime transformation adds ~20-50ms per module on first load
- Not recommended for production use
- Cannot transform `eval()` or `vm.runInContext()` code
- May conflict with other custom loaders

### Features

#### Identify Users

You can also associate data (logs, exceptions, and network requests) with specific users using the identify method. This is particularly helpful for user-centric debugging.

#### Identify Signature

Here's the argument signature:

```ts
type Identify = (
  userId: string,
  traits: Record<string, any>,
  override: boolean,
) => void;
```

#### Identify Example


In order to identify, you will need to call the `identify` method. You can also force an override of stored values by setting the `override` argument to `true`.

```ts title="Link a user to a recording"
import { identify } from "@sailfish-ai/sf-veritas";

identify("user-123", { lastAction: "2024-11-18" });
```

#### Add or Update Metadata

In order to add or update metadata associated with specific users and the recordings, you can use the `addOrUpdateMetadata` method. This will associate all information with the recording by calling this method on any Node.js backend.

- **`userId`**: `string`

  - This associates the logs (and recording, if there is an associated recording) with a specific user.

- **`traits`**: `Record<string, any>`

  - This is a custom dictionary of traits to values for a specific user or customer. Everything is searchable!

- **`traitsJson`**: `string`

  - This is a custom JSON, just like `traits`, but using the JSON format.

- **`override`**: `boolean`
  - This tells whether any of the information we pass along should be overridden or not.

#### AddOrUpdateMetadata Signature

```ts
type AddOrUpdateMetadataFunction = (
  userId: string,
  traits: Record<string, any>,
  override: boolean,
) => void;
```

#### Add/Update Metadata Example

```ts title="Add/Update Metadata for a recording"
import { addOrUpdateMetadata } from "@sailfish-ai/sf-veritas";

addOrUpdateMetadata("user-123", { birthday: "2000-01-01" }, true);
```

### Technical Details

- **Non-Blocking Execution**: All operations are executed in non-blocking, isolated threads, ensuring less than 20 µs of overhead.
- **Contextual Awareness**: Tracks context across threads, processes, and async tasks to associate logs with the correct sequence of events.

By following these steps, you can seamlessly integrate `sf-veritas` into your Node.js project and start capturing LEaPS (Logs, Exceptions, and Print Statements) effectively!