Titan Planet Logo
Knowledge

6. Node Compatibility

Enable Node.js core APIs and npm library support in TitanPL applications.

@titanpl/node

Node.js compatibility layer for the TitanPL Gravity Runtime.

This package enables selected Node.js core APIs to work inside TitanPL applications by mapping them to Titan’s native Rust-powered runtime. It allows many existing npm libraries (sync-based) to run inside TitanPL without modification.


Why This Exists

TitanPL runs on:

  • V8 isolates
  • Rust core Gravity runtime
  • No Node.js process
  • No libuv
  • No Node event loop

Because of this, Node built-ins like fs, crypto, or process do not exist by default. @titanpl/node provides compatibility shims that redirect Node imports to Titan-native implementations.


πŸš€ Quick Start (Important)

To enable Node compatibility in an action, import the globals shim first:

import "@titanpl/node/globals";

This sets up:

  • process
  • Buffer
  • global compatibility utilities

Then you can use Node-style imports normally:

import fs from "fs";
import path from "path";

Supported Core Modules (Sync APIs)

The following modules are mapped to Titan's native implementations:

Node APITitan Core MappingCurrent Status
fst.fsFull (Sync)
patht.pathFull
cryptot.cryptoFull
processt.procPartial
ost.osPartial
Buffert.bufferFull
utilPure JS ShimsPartial
eventsbasic EventEmitterStable

Sync Only

Only synchronous APIs are supported. Async Node APIs (callbacks/promises) are not supported because Titan uses drift() for async orchestration.


Example β€” Basic File Usage

import "@titanpl/node/globals";
import fs from "fs";
import path from "path";

export const hello = defineAction(() => {
  const file = path.join("data", "hello.txt");

  fs.writeFileSync(file, "Hello Titan");
  const content = fs.readFileSync(file, "utf-8");

  return { content };
});

πŸ“¦ Supported npm Libraries

Many popular utility libraries work out-of-the-box because they rely on synchronous logic or the core modules listed above.

1. Environment Variables (dotenv)

Since dotenv uses fs.readFileSync and process.env, it works perfectly.

import "@titanpl/node/globals";
import dotenv from "dotenv";

dotenv.config();

export const config = defineAction(() => {
  return { apiKey: process.env.API_KEY };
});

2. Password Hashing (bcryptjs)

bcryptjs is a pure JavaScript implementation of bcrypt that works with Titan's random number generators.

import "@titanpl/node/globals";
import bcrypt from "bcryptjs";

export const hash = defineAction(() => {
  const salt = bcrypt.genSaltSync(10);
  const hash = bcrypt.hashSync("secret-password", salt);
  
  return { 
    hash,
    isValid: bcrypt.compareSync("secret-password", hash)
  };
});

3. Querying with GraphQL

You can use graphql to parse schemas and execute queries synchronously against local data or in-memory resolvers.

import "@titanpl/node/globals";
import { graphql, buildSchema } from "graphql";

const schema = buildSchema(`
  type Query {
    hello: String
  }
`);

const rootValue = { hello: () => "Hello from Titan GraphQL!" };

export const runQuery = defineAction(() => {
  // Note: We use the sync execution pattern
  const response = graphql({
    schema,
    source: "{ hello }",
    rootValue
  });

  return response;
});

4. Schema Validation (zod or joi)

These libraries are pure JavaScript and work seamlessly for validating inputs in your Titan Actions.

import "@titanpl/node/globals";
import { z } from "zod";

const UserSchema = z.object({
  id: z.string().uuid(),
  name: z.string().min(3)
});

export const validate = defineAction((input) => {
  const result = UserSchema.safeParse(input);
  return result;
});

More Examples

Day.js

import "@titanpl/node/globals";
import dayjs from "dayjs";

export const now = defineAction(() => {
  return { time: dayjs().format() };
});

Lodash

import "@titanpl/node/globals";
import _ from "lodash";

export const shuffle = defineAction(() => {
  return { arr: _.shuffle([1,2,3,4,5]) };
});

UUID

import "@titanpl/node/globals";
import { v4 as uuid } from "uuid";

export const id = defineAction(() => {
  return { id: uuid() };
});

How It Works

During bundling, Titan rewrites imports like:

import fs from "fs";

into:

import fs from "@titanpl/node/fs";

This ensures zero overhead at runtime while preserving the developer experience you're used to in Node.js.


Detailed API Surface

Globals (@titanpl/node/globals)

Once imported, the following globals are injected into the environment:

  • process β€” Full process shim with env, argv, and pid.
  • Buffer β€” The classic Node-style Buffer for binary data handling.
  • __dirname β€” Maps to the current directory (project root during execution).
  • crypto β€” WebCrypto-compatible surface exposing getRandomValues and randomUUID.

fs (File System)

The fs module is a direct mapping to t.fs methods, targeting synchronous Node.js aliases.

  • readFileSync(path, encoding?) β€” Reads file contents.
  • writeFileSync(path, data) β€” Writes data to a file.
  • existsSync(path) β€” Checks if a path exists.
  • readdirSync(path) β€” Lists directory entries.
  • mkdirSync(path) β€” Creates directories.
  • statSync(path) β€” Returns metadata object.
  • rmSync(path) β€” Deletes a file or directory.

path

Full utility module for cross-platform path manipulation.

  • join(...parts)
  • resolve(...parts)
  • dirname(path)
  • basename(path)
  • extname(path)

crypto

Full Node-style crypto utilities backed by Titan's Rust core.

  • createHash(algo) β€” Supporting sha256, sha512, md5.
  • createHmac(algo, key) β€” High-performance HMAC hashing.
  • randomBytes(size) β€” Generates cryptographically secure bytes.
  • randomUUID() β€” Native UUID v4 generator.
  • createCipheriv(algo, key, iv) β€” Encrypt data using native Rust crypto.
  • createDecipheriv(algo, key, iv) β€” Decrypt data.
  • timingSafeEqual(a, b) β€” Constant-time string comparison.

buffer

The Buffer shim provides compatibility for libraries expecting the Node.js Buffer class.

  • Buffer.from(input, encoding?) β€” Support for UTF-8 and Base64 source.
  • Buffer.toString(bytes, encoding?) β€” Conversions to shell strings or data strings.

util

Lightweight utility shims for common internal logic.

  • inspect(value) β€” JSON-based visualizer for debugging.
  • inherits(ctor, superCtor) β€” Proto-based inheritance.
  • format(...args) β€” String formatter for logs and outputs.

🚫 What Will NOT Work

Titan is not Node.js β€” it is a native Rust server running V8. The following will NOT work:

  • child_process (Titan handles processes via native extensions if needed)
  • cluster (Titan multi-threaded via Rust, not JS clusters)
  • Native .node addons (Use Titan Rust Extensions instead)
  • Streams requiring real Node internals (libuv based)
  • Async Node APIs (Use drift() for async)
  • Libraries that depend heavily on the Node event loop

Timers & Microtasks

Because Titan executes in a strictly synchronous request-response cycle (only broken by drift), standard async timers are purposefully blocked to prevent hangs:

  • setTimeout, setInterval, setImmediate
  • process.nextTick, queueMicrotask

Calling these will throw an error with the [Titan Node Libs Support] explanation.


Installation

npm install @titanpl/node

Always place the globals import at the very top of your action file:

import "@titanpl/node/globals";

On this page