Source: engine/native/node/native-capabilities/src/index.js

const NativeModule = require('@proceed/native-module');
const fs = require('fs');
const path = require('path');

/**
 * **Capabilities**
 *
 * All available (IoT-related) capabilities are registered here. You can easily
 * add functionality by implementing a custom module and adding it to the
 * `capabilities` directory. This module automatically registers all the modules
 * in that directory.
 *
 * @class
 */
class Capabilities extends NativeModule {
  constructor(options) {
    super();

    this.commands = ['capability', 'allCapabilities'];
    // Store for all the functions
    this._functions = new Map();
    this._names = [];

    // Create the capabilities folder if it doesn't exist yet
    this._path = (options && options.dir) || __dirname;
    try {
      fs.statSync(`${this._path}/capabilities/`);
    } catch (err) {
      if (err.code === 'ENOENT') {
        return fs.mkdirSync(`${this._path}/capabilities/`);
      }
      throw err;
    }

    this._setFunctions();
  }

  /**
   *
   * @param {*} command
   * @param {*} args
   * @param {*} send
   */
  executeCommand(command, args, send) {
    if (command === 'capability') {
      return this.handleCapabilityTask(args, send);
    }
    if (command === 'allCapabilities') {
      return this.allCapabilities();
    }
    return undefined;
  }

  /**
   * Set the available functions
   * @memberof Capabilities
   */
  _setFunctions() {
    const capPath = path.join(this._path, './capabilities/'); // FIXME: require.main.filename ?
    const files = fs.readdirSync(capPath, { withFileTypes: true });
    files
      .filter((file) => file.isDirectory())
      .forEach((dir) => {
        // eslint-disable-next-line global-require, import/no-dynamic-require
        const jsonld = fs.readFileSync(`${capPath + dir.name}/semantic.jsonld`, 'utf-8');
        this._functions.set(capPath + dir.name, jsonld);
      });
  }

  /**
   * Receives the iot-task message from the dpe and calls
   * the corresponding function if it was registered.
   * @memberof Capabilities
   * @param {array} task The taskID and args for the iot-task
   */
  handleCapabilityTask(args, send) {
    // The semantic task description and args for the function
    const [description, taskArgs] = args;
    // Execute the function if it's available
    /* eslint-disable global-require, import/no-dynamic-require */
    const func = eval('require(description)');
    if (typeof func === 'function') {
      func(taskArgs, this.respond.bind(this, send));
    } else {
      this.respond(send, ['Not implemented!']);
    }
  }

  /**
   * @returns {list} all capabilities
   */
  async allCapabilities() {
    const entries = Array.from(this._functions.entries());
    const allCapabilities = entries.map(([id, jsonld]) => ({
      identifier: id,
      semanticDescription: jsonld,
    }));
    return [allCapabilities];
  }

  /**
   * Send the response back to the native part of the PROCEED dispatcher.
   * @memberof Capabilities
   * @param {string} taskID The taskID for the task
   * @param {array} res The response array [err, ...args]
   */
  respond(send, res) {
    send(...res);
  }
}

module.exports = Capabilities;