# Node.js API compatibility - Utils

The `util` module in Node.js provides a set of utility functions that assist with various tasks, enhancing the functionality of JavaScript and Node.js applications. It's particularly useful for developers looking to simplify common programming tasks and improve code readability. This module is available in the Azion Runtime through Node.js compatibility, where helpers such as `promisify` and `inspect` are handy for adapting callback-based code and for logging structured data inside a function.

---

## Example: Promisify and inspect

The example below shows how to use the `util` module in a function:

```javascript
/**
 * An example of using Node.js Util API in an Azion Function.
 * Support:
 * - Partially supported (Extended by library `util`)
 * @module runtime-apis/nodejs/util/main
 * @example
 * // Execute with Azion Bundler:
 * npx edge-functions build
 * npx edge-functions dev
 */
import util from "node:util";

const myTest = (callback) => {
  try {
    callback(null, "Success!");
  } catch (err) {
    callback(err);
  }
};

/**
 * An example of using the Node.js Util API in an Azion Function.
 * @param {*} event
 * @returns {Promise<Response>}
 */
const main = async (event) => {
  const promisifyTest = util.promisify(myTest);
  const result = await promisifyTest();
  console.log(util.inspect(result, { showHidden: false, depth: null }));

  return new Response("Done!", { status: 200 });
};

export default main;
```

---

## Example: Converting callbacks to promises

Use `promisify` to convert callback-style functions to async/await:

```javascript
import util from "node:util";
import { setTimeout } from "node:timers";

// Simulate a callback-based API
const fetchData = (id, callback) => {
  setTimeout(() => {
    if (id > 0) {
      callback(null, { id, data: `Item ${id}`, timestamp: Date.now() });
    } else {
      callback(new Error("Invalid ID"));
    }
  }, 100);
};

// Convert to promise-based
const fetchDataAsync = util.promisify(fetchData);

const main = async (event) => {
  try {
    // Now can use with async/await
    const result1 = await fetchDataAsync(1);
    const result2 = await fetchDataAsync(2);

    console.log("Result 1:", result1);
    console.log("Result 2:", result2);

    return new Response(JSON.stringify({ results: [result1, result2] }), {
      headers: { "Content-Type": "application/json" }
    });
  } catch (error) {
    console.error("Error:", error.message);
    return new Response(JSON.stringify({ error: error.message }), {
      status: 400,
      headers: { "Content-Type": "application/json" }
    });
  }
};

export default main;
```

---

## Example: Deep object inspection

Use `inspect` for detailed object logging and debugging:

```javascript
import util from "node:util";

const main = async (event) => {
  // Complex object to inspect
  const requestInfo = {
    url: event.request.url,
    method: event.request.method,
    headers: Object.fromEntries(event.request.headers),
    timestamp: new Date().toISOString()
  };

  // Nested object
  const complexData = {
    level1: {
      level2: {
        level3: {
          value: "deep",
          array: [1, 2, { nested: true }]
        }
      }
    }
  };

  // Inspect with options
  const inspected = util.inspect(requestInfo, {
    depth: null,        // Unlimited depth
    colors: false,      // Explicitly disabled since output goes into JSON response
    compact: false,     // Multi-line output
    showHidden: false,  // Don't show non-enumerable
    maxArrayLength: 10  // Limit array display
  });

  console.log("Request info:", inspected);

  // Format string with placeholders
  const formatted = util.format(
    "Request %s to %s at %s",
    requestInfo.method,
    requestInfo.url,
    requestInfo.timestamp
  );
  console.log(formatted);

  return new Response(JSON.stringify({
    requestInfo,
    formatted,
    inspectedDepth: util.inspect(complexData, { depth: 2 })
  }), {
    headers: { "Content-Type": "application/json" }
  });
};

export default main;
```

---

## Example: Type checking utilities

Use `util.types` for runtime type checking:

```javascript
import util from "node:util";

const main = async (event) => {
  const values = {
    string: "hello",
    number: 42,
    boolean: true,
    object: { key: "value" },
    array: [1, 2, 3],
    null: null,
    undefined: undefined,
    date: new Date(),
    regex: /test/,
    promise: Promise.resolve("test"),
    buffer: new ArrayBuffer(8)
  };

  // Type checking results
  const typeChecks = {
    isDate: util.types.isDate(values.date),
    isRegExp: util.types.isRegExp(values.regex),
    isPromise: util.types.isPromise(values.promise),
    isArrayBuffer: util.types.isArrayBuffer(values.buffer),
    isNativeError: util.types.isNativeError(new Error("test")),
    isTypedArray: util.types.isTypedArray(new Uint8Array(4)),
    isMap: util.types.isMap(new Map()),
    isSet: util.types.isSet(new Set())
  };

  // Note: isConstructor is not part of util.types API
  // Use typeof for function/constructor checking
  class MyClass {}
  const isFunction = typeof MyClass === "function";

  console.log("Type checks:", typeChecks);

  return new Response(JSON.stringify({
    values: Object.keys(values).map(k => ({
      key: k,
      type: typeof values[k],
      isDate: util.types.isDate(values[k]),
      isRegExp: util.types.isRegExp(values[k])
    })),
    typeChecks,
    isFunction
  }), {
    headers: { "Content-Type": "application/json" }
  });
};

export default main;
```

---

## Example: String formatting and deprecation

Use format strings and mark deprecated functions:

```javascript
import util from "node:util";

// Mark a function as deprecated
const oldFunction = util.deprecate(
  () => "This is the old function",
  "oldFunction is deprecated. Use newFunction instead."
);

const newFunction = () => "This is the new function";

const main = async (event) => {
  const results = [];

  // String formatting with %s, %d, %j, %%
  results.push(util.format("Hello %s!", "World"));
  results.push(util.format("Number: %d, String: %s", 42, "test"));
  results.push(util.format("JSON: %j", { key: "value" }));
  results.push(util.format("Percent: %%"));

  // Style strings (for console output)
  // Note: ANSI codes are only meaningful in terminal output
  // They will appear as escape sequences in HTTP responses
  const styled = util.format(
    "%s %s %s",
    "\x1b[31mRed\x1b[0m",
    "\x1b[32mGreen\x1b[0m",
    "\x1b[34mBlue\x1b[0m"
  );
  console.log(styled); // Useful in logs only

  // Call deprecated function (will show warning in console)
  const oldResult = oldFunction();
  const newResult = newFunction();

  // Get object's own property names
  const obj = { a: 1, b: 2, c: 3 };
  const keys = Object.keys(obj);

  return new Response(JSON.stringify({
    formattedStrings: results,
    oldFunctionResult: oldResult,
    newFunctionResult: newResult,
    objectKeys: keys
    // Note: styled string not included - ANSI codes not useful in JSON
  }), {
    headers: { "Content-Type": "application/json" }
  });
};

export default main;
```

---

## Example: Inheritance and class utilities

Use `inherits` for prototypal inheritance patterns:

```javascript
import util from "node:util";
import { EventEmitter } from "node:events";

// ⚠️ Note: util.inherits() is deprecated since Node.js v5.0.0
// Prefer ES6 class syntax instead:
// class CustomEmitter extends EventEmitter {
//   constructor() {
//     super();
//     this.events = [];
//   }
// }

// Legacy pattern shown for compatibility with older codebases
// Create a custom class that inherits from EventEmitter
function CustomEmitter() {
  EventEmitter.call(this);
  this.events = [];
}

// Inherit from EventEmitter (deprecated pattern)
util.inherits(CustomEmitter, EventEmitter);

// Add custom methods
CustomEmitter.prototype.recordEvent = function(name, data) {
  this.events.push({ name, data, timestamp: Date.now() });
  this.emit(name, data);
};

const main = async (event) => {
  const emitter = new CustomEmitter();
  const recordedEvents = [];

  // Listen to events
  emitter.on("data", (data) => {
    console.log("Received data:", data);
    recordedEvents.push({ type: "data", value: data });
  });

  emitter.on("complete", () => {
    console.log("Process complete");
    recordedEvents.push({ type: "complete" });
  });

  // Emit events
  emitter.recordEvent("data", { id: 1, message: "First" });
  emitter.recordEvent("data", { id: 2, message: "Second" });
  emitter.recordEvent("complete", null);

  // Check inheritance
  const isEmitter = emitter instanceof EventEmitter;

  return new Response(JSON.stringify({
    recordedEvents,
    emitterEvents: emitter.events,
    isEventEmitter: isEmitter
  }), {
    headers: { "Content-Type": "application/json" }
  });
};

export default main;
```

---

## Supported APIs

| API | Status |
|-----|--------|
| `util.promisify()` | 🟢 Supported |
| `util.inspect()` | 🟢 Supported |
| `util.format()` | 🟢 Supported |
| `util.deprecate()` | 🟢 Supported |
| `util.isDeepStrictEqual()` | 🟢 Supported |
| `util.inherits()` | 🟡 Deprecated (use ES6 classes) |
| `util.callbackify()` | 🟡 Partially supported |
| `util.formatWithOptions()` | 🟡 Partially supported |
| `util.getSystemErrorMap()` | 🟡 Partially supported |
| `util.getSystemErrorName()` | 🟡 Partially supported |
| `util.log()` | 🟡 Partially supported |
| `util.stripVTControlCharacters()` | 🟡 Partially supported |
| `util.styleText()` | 🟡 Partially supported |
| `util.types.isDate()` | 🟢 Supported |
| `util.types.isRegExp()` | 🟢 Supported |
| `util.types.isPromise()` | 🟢 Supported |
| `util.types.isArrayBuffer()` | 🟢 Supported |
| `util.types.isNativeError()` | 🟢 Supported |
| `util.types.isTypedArray()` | 🟢 Supported |
| `util.types.isMap()` | 🟢 Supported |
| `util.types.isSet()` | 🟢 Supported |
| `util.types.isCryptoKey()` | 🟢 Supported |
| `util.types.isKeyObject()` | 🟢 Supported |

> **Note:** APIs marked as 🟡 Partially supported have limited functionality compared to the full Node.js implementation. Note that `util.inherits()` is deprecated since Node.js v5.0.0 — prefer ES6 `class` syntax with `extends` for inheritance. For type checking, `util.types` provides runtime-safe alternatives to `instanceof` checks.

---

## Related resources

- [Node.js `util` documentation](https://nodejs.org/api/util.html)