function Task(ctx, tag, isTX, config) {

    const $p = config.promise;

    /**
     * @member {TaskContext} Task#ctx
     * @readonly
     * @description
     * Task/Transaction Context object - contains individual properties for each task/transaction.
     *
     * @see event {@link event:query query}
     *
     * @example
     *
     * db.task(t => {
     *     return t.ctx; // task context object
     * })
     *     .then(ctx => {
     *         console.log('Task Duration:', ctx.duration);
     *     });
     *
     * @example
     *
     * db.tx(t => {
     *     return t.ctx; // transaction context object
     * })
     *     .then(ctx => {
     *         console.log('Transaction Duration:', ctx.duration);
     *     });
     */
    this.ctx = ctx.ctx = {}; // task context object;

    npm.utils.addReadProp(this.ctx, 'isTX', isTX);

    if ('context' in ctx) {
        npm.utils.addReadProp(this.ctx, 'context', ctx.context);
    }

    npm.utils.addReadProp(this.ctx, 'connected', !ctx.db);
    npm.utils.addReadProp(this.ctx, 'tag', tag);
    npm.utils.addReadProp(this.ctx, 'dc', ctx.dc);
    npm.utils.addReadProp(this.ctx, 'level', ctx.level);
    npm.utils.addReadProp(this.ctx, 'inTransaction', ctx.inTransaction);

    if (isTX) {
        npm.utils.addReadProp(this.ctx, 'txLevel', ctx.txLevel);
    }

    npm.utils.addReadProp(this.ctx, 'parent', ctx.parentCtx);

    // generic query method;
    this.query = function (query, values, qrm) {
        if (!ctx.db) {
            return $p.reject(new Error(npm.text.looseQuery));
        }
        return config.$npm.query.call(this, ctx, query, values, qrm);
    };

    /**
     * @deprecated
     * Consider using <b>async/await</b> syntax instead, or if you must have
     * pre-generated promises, then $[Promise.allSettled].
     *
     * @method Task#batch
     * @description
     * Settles a predefined array of mixed values by redirecting to method $[spex.batch].
     *
     * For complete method documentation see $[spex.batch].
     *
     * @param {array} values
     * @param {Object} [options]
     * Optional Parameters.
     * @param {function} [options.cb]
     *
     * @returns {external:Promise}
     */
    this.batch = function (values, options) {
        return config.$npm.spex.batch.call(this, values, options);
    };

    /**
     * @method Task#page
     * @description
     * Resolves a dynamic sequence of arrays/pages with mixed values, by redirecting to method $[spex.page].
     *
     * For complete method documentation see $[spex.page].
     *
     * @param {function} source
     * @param {Object} [options]
     * Optional Parameters.
     * @param {function} [options.dest]
     * @param {number} [options.limit=0]
     *
     * @returns {external:Promise}
     */
    this.page = function (source, options) {
        return config.$npm.spex.page.call(this, source, options);
    };

    /**
     * @method Task#sequence
     * @description
     * Resolves a dynamic sequence of mixed values by redirecting to method $[spex.sequence].
     *
     * For complete method documentation see $[spex.sequence].
     *
     * @param {function} source
     * @param {Object} [options]
     * Optional Parameters.
     * @param {function} [options.dest]
     * @param {number} [options.limit=0]
     * @param {boolean} [options.track=false]
     *
     * @returns {external:Promise}
     */
    this.sequence = function (source, options) {
        return config.$npm.spex.sequence.call(this, source, options);
    };

}
@interface Task
@description
Extends {@link Database} for an automatic connection session, with methods for executing multiple database queries.

The type isn't available directly, it can only be created via methods {@link Database#task Database.task}, {@link Database#tx Database.tx},
or their derivations.

When executing more than one request at a time, one should allocate and release the connection only once,
while executing all the required queries within the same connection session. More importantly, a transaction
can only work within a single connection.

This is an interface for tasks/transactions to implement a connection session, during which you can
execute multiple queries against the same connection that's released automatically when the task/transaction is finished.

Each task/transaction manages the connection automatically. When executed on the root {@link Database} object, the connection
is allocated from the pool, and once the method's callback has finished, the connection is released back to the pool.
However, when invoked inside another task or transaction, the method reuses the parent connection.

@see
{@link Task#ctx ctx},
{@link Task#batch batch},
{@link Task#sequence sequence},
{@link Task#page page}

@example
db.task(t => {
      // t = task protocol context;
      // t.ctx = Task Context;
      return t.one('select * from users where id=$1', 123)
          .then(user => {
              return t.any('select * from events where login=$1', user.name);
          });
  })
.then(events => {
      // success;
  })
.catch(error => {
      // error;
  });