# Compatibilidade APIs Node.js - Utils

O módulo `util` no Node.js fornece um conjunto de funções utilitárias que auxiliam em várias tarefas, aprimorando a funcionalidade de aplicações JavaScript e Node.js. É particularmente útil para desenvolvedores que buscam simplificar tarefas comuns de programação e melhorar a legibilidade do código. Este módulo está disponível no Azion Runtime através da compatibilidade com Node.js, onde helpers como `promisify` e `inspect` são úteis para adaptar código baseado em callbacks e para registrar dados estruturados dentro de uma function.

---

## Exemplo: Promisify e inspect

O exemplo abaixo mostra como usar o módulo `util` em uma 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;
```

---

## Exemplo: Convertendo callbacks para promises

Use `promisify` para converter funções estilo callback para 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;
```

---

## Exemplo: Inspeção profunda de objetos

Use `inspect` para registro detalhado de objetos e depuração:

```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;
```

---

## Exemplo: Utilitários de verificação de tipo

Use `util.types` para verificação de tipo em runtime:

```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;
```

---

## Exemplo: Formatação de strings e depreciação

Use strings de formato e marque funções como depreciadas:

```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;
```

---

## Exemplo: Herança e utilitários de classe

Use `inherits` para padrões de herança prototipal:

```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;
```

---

## APIs com suporte

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

:::note
APIs marcadas como 🟡 Parcialmente suportado têm funcionalidade limitada em comparação com a implementação completa do Node.js. Note que `util.inherits()` está depreciado desde Node.js v5.0.0 — prefira a sintaxe de `class` do ES6 com `extends` para herança. Para verificação de tipo, `util.types` fornece alternativas seguras em runtime às verificações com `instanceof`.
:::

---

## Recursos relacionados

- [Documentação do `util` no Node.js](https://nodejs.org/api/util.html)