{"id":15011306,"url":"https://github.com/rootslab/spade","last_synced_at":"2025-10-20T11:39:33.913Z","repository":{"id":17154134,"uuid":"19920971","full_name":"rootslab/spade","owner":"rootslab","description":"♠ Spade, a robust, full-featured, multi-module client for Redis.","archived":false,"fork":false,"pushed_at":"2018-02-10T14:41:22.000Z","size":416,"stargazers_count":52,"open_issues_count":0,"forks_count":1,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-23T21:02:58.340Z","etag":null,"topics":["lua-cache","node-js","nodejs","redis","redis-client","redis-command","redis-lua-script","redis-protocol"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rootslab.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-05-18T21:00:48.000Z","updated_at":"2023-07-01T19:06:03.000Z","dependencies_parsed_at":"2022-09-26T21:21:42.155Z","dependency_job_id":null,"html_url":"https://github.com/rootslab/spade","commit_stats":null,"previous_names":[],"tags_count":243,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rootslab%2Fspade","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rootslab%2Fspade/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rootslab%2Fspade/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rootslab%2Fspade/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rootslab","download_url":"https://codeload.github.com/rootslab/spade/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247838416,"owners_count":21004575,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["lua-cache","node-js","nodejs","redis","redis-client","redis-command","redis-lua-script","redis-protocol"],"created_at":"2024-09-24T19:40:32.897Z","updated_at":"2025-10-20T11:39:28.850Z","avatar_url":"https://github.com/rootslab.png","language":"JavaScript","readme":"### ♠ Spade\n\n[![NPM VERSION](http://img.shields.io/npm/v/spade.svg?style=flat)](https://www.npmjs.org/package/spade)\n[![CODACY BADGE](https://img.shields.io/codacy/b18ed7d95b0a4707a0ff7b88b30d3def.svg?style=flat)](https://www.codacy.com/public/44gatti/spade)\n[![CODECLIMATE-TEST-COVERAGE](https://img.shields.io/codeclimate/c/rootslab/spade.svg?style=flat)](https://codeclimate.com/github/rootslab/spade)\n[![LICENSE](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/rootslab/spade#mit-license)\n\n![NODE VERSION](https://img.shields.io/node/v/spade.svg)\n[![TRAVIS CI BUILD](http://img.shields.io/travis/rootslab/spade.svg?style=flat)](http://travis-ci.org/rootslab/spade)\n[![BUILD STATUS](http://img.shields.io/david/rootslab/spade.svg?style=flat)](https://david-dm.org/rootslab/spade)\n[![DEVDEPENDENCY STATUS](http://img.shields.io/david/dev/rootslab/spade.svg?style=flat)](https://david-dm.org/rootslab/spade#info=devDependencies)\n\n[![NPM MONTHLY](http://img.shields.io/npm/dm/spade.svg?style=flat)](http://npm-stat.com/charts.html?package=spade)\n![NPM YEARLY](https://img.shields.io/npm/dy/spade.svg)\n![NPM TOTAL](https://img.shields.io/npm/dt/syllabus.svg)\n\n[![NPM GRAPH](https://nodei.co/npm/spade.png?downloads=true\u0026downloadRank=true\u0026stars=true)](https://nodei.co/npm/spade/)\n\n\u003e ♠ __Spade__, a robust, full-featured, multi-module, __Redis__ client:\n - It offers the ability to restrict __commands to a particular Redis version__ via the _**semver**_\n   constructor option. Specifying this option turns __Spade__ in _develop mode_, it enables a series\n   of mix-ins to get brief descriptions of every implemented command.\n - It implements a simple __delayed mechanism for re-connecting to socket__ when the client connection was not voluntarily interrupted.\n - It collects commands in the __queue__ also when the client is __offline__.\n - It implements an automatic __command rollback__ mechanism for __subscriptions__ when connection is lost and becames ready again.\n - It implements __AUTH__ logic and __automatic db SELECT__ on socket (re)connection, configurable\n   via the _**security**_ constructor option.\n - It offers automatic __LUA scripts caching__, using a simple __NFU__ with __linear aging__ eviction\n   algorithm ( __NFU__ stands for _Not Frequently Used_ ).\n - It __correctly handles multiple (p)(un)subscription__ commands as we will expect (1 command : multiple replies : multiple callback execution); it was well tested against some weird edge cases.\n See __[tests](#run-tests)__ for pubsub.\n - It supports the new __PING__ command signature also in __PubSub mode__.\n - It implements a __polling mechanism__, useful to force __automatic re-connection__ when client hangs while in __PubSub mode__.\n - It facilitates scanning of Redis keyspace, implementing some simple iterators for __SCAN__/__HSCAN__/__SSCAN__/__ZSCAN__ commands. See [#loadIterators](#loaditerators).\n\n\u003e ♠ __Spade__ makes use of some __well tested__ modules:\n - __[Σ Syllabus](https://github.com/rootslab/syllabus)__ module for __command encoding__ and __command helpers mix-ins__, it  also offers a series of __helpers functions__ to convert a raw data reply in a usable format.\n \u003e Internally it uses __[Hoar](https://github.com/rootslab/hoar)__ module to handle __Semantic Versioning 2.0__, __[Sermone](https://github.com/rootslab/sermone)__ to encode commands, __[Abaco](https://github.com/rootslab/abaco)__ and __[Bolgia](https://github.com/rootslab/bolgia)__ modules to get some utilities. Moreover, __Syllabus__ mantains a __cache__ for __LUA__ scripts, using the __[Camphora](https://github.com/rootslab/camphora)__ module.\n - __[♎ Libra](https://github.com/rootslab/libra)__ module to handle bindings between commands which have been sent and relative __Redis__ replies; it handles also __commands queue rollbacks__ with the help of __[Train](https://github.com/rootslab/train)__ module.\n - __[Cocker](https://github.com/rootslab/cocker)__ module to properly handle __socket reconnection__ when the connection is lost. \n - __[Hiboris](https://github.com/rootslab/hiboris)__, a utility module to load  __[hiredis](https://github.com/redis/hiredis-node)__ _native parser_, or to fall back to __[Boris](https://github.com/rootslab/boris)__, a _pure js parser_ module for __Redis__ string protocol; internally _Boris_ uses __[Peela](https://github.com/rootslab/peela)__ as command stack.\n - __[Cucu](https://github.com/rootslab/cucu)__, a tiny module to handle the __scheduled execution of repetitive methods/tasks__.\n - __[Gerry](https://github.com/rootslab/gerry)__, a tiny module to handle __event logging__ to console, for debugging and testing purpose.\n - __[Dado](https://github.com/rootslab/dado)__, for running tests.\n - __[Vapid](https://github.com/rootslab/vapid)__, a vacuous __Redis__ implementation, with fully functional PubSub system.\n\n\u003e __NOTE__ : If you need a __minimal Redis client__ based on __♠ Spade__ code, __specific for PubSub and Monitor mode__ try 🂢 __[Deuces](https://github.com/rootslab/deuces)__.\n\n### Table of Contents\n\n- __[Install](#install)__\n- __[Run Tests](#run-tests)__\n- __[Run Benchmarks](#run-benchmarks)__\n- __[Constructor](#constructor)__\n   - __[Options](#options)__\n- __[Properties](#properties)__\n- __[Methods](#methods)__\n   - __[#cli](#cli)__\n   - __[#connect](#connect)__\n   - __[#disconnect](#disconnect)__\n   - __[#initCache](#initcache)__\n   - __[#initTasks](#inittasks)__\n      - __[polling task](#polling-task)__\n   - __[#loadIterators](#loaditerators)__\n   - __[Redis Commands](#redis-commands)__\n   - __[Command Callback](#command-callback)__\n   - __[Interactive Mode](#interactive-mode)__\n   - __[LUA Cache and SCRIPT Methods](#lua-cache-and-script-methods)__\n- __[Events](#events)__\n   - __[Events Sequence Diagram](#events-sequence-diagram)__\n   - __[Error Events](#error-events)__\n   - __[Auth Events](#auth-events)__\n   - __[Select Events](#select-events)__\n   - __[Script Cache Events](#script-cache-events)__\n   - __[Socket Connection Events](#socket-connection-events)__\n   - __[PubSub Events](#pubsub-events)__\n   - __[Monitor Events](#monitor-events)__\n   - __[Tasks Events](#tasks-events)__\n      - __[polling events](#polling-events)__\n   - __[Other Debug Events](#other-debug-events)__\n      - __[iterators events](#iterators-events)__\n- __[MIT License](#mit-license)__\n\n-----------------------------------------------------------------------\n\n### Install\n\n\u003e __NOTE:__ only __node__ engines **\"\u003e=v0.10.x\"** are supported.\n\n```bash\n $ npm install spade [-g]\n // clone repo\n $ git clone git@github.com:rootslab/spade.git\n```\n\u003e __install and update devDependencies__:\n\n```bash\n $ cd spade/\n $ npm install\n # update\n $ npm update\n```\n\u003e __require__\n\n```javascript\nvar Spade = require( 'spade' );\n```\n\u003e See [examples](example/).\n\n### Run Tests\n\n\u003e __to run all test files, install devDependecies:__\n\n```bash\n $ cd spade/\n # install or update devDependecies \n $ npm install \n # run tests\n $ npm test\n```\n\u003e __to execute a single test file simply do__:\n\n```bash\n $ node test/file-name.js\n```\n\u003e __NOTE__:\n\u003e  - tests need a running __[Redis](http://redis.io/)__ server instance, with default/stock configuration ( port __6379__ ).\n\u003e  - for some connection tests you need the __[Vapid](https://github.com/rootslab/vapid)__ **_devDependency_**, a vacuous __Redis__ server module ( port __6380__ ).\n\n### Run Benchmarks\n\n\u003e run benchmarks for __spade__ (fast/tight checks/turtle developer).\n\n```bash\n $ cd spade/\n $ npm run bench\n```\n\u003e run benchmarks for **node_redis** (faster/loose checks/an army of contributors).\n\n```bash\n $ cd spade/\n $ npm install redis\n $ npm run node_redis_bench\n```\n\n\u003e run benchmarks for **ioredis** (slowest/loose checks/unsafe pubsub).\n\n```bash\n $ cd spade/\n $ npm install ioredis\n $ npm run ioredis_bench\n```\n\n\u003e __NOTE__:\n\u003e  - benchmarks need a running __Redis__ server instance, with default/stock configuration.\n\u003e  - to switch to the faster __hiredis__ native parser, install **_devDependencies_** .\n\n----------------------------------------------------------------------------------------------\n\n### Constructor\n\n\u003e Create an instance, the argument within [ ] is optional.\n\n```javascript\nSpade( [ Object opt ] )\n// or\nnew Spade( [ Object opt ] )\n```\n\n### Options\n\n\u003e Default options are listed.\n\n```javascript\nopt = {\n    /*\n     * Syllabus option to enable develop mode and restrict\n     * commands to a particular Redis semantic version.\n     * Use:\n     * - boolean 'true' or string '*' for all commands available.\n     * - semver string to restrict commands ( like '1.0.0' ).\n     *\n     * NOTE, develop mode enables some utility methods to get command\n     * descriptions, like:\n     * - Spade.mixins#info( String command ) get cmd infos\n     * - Spade.mixins#size() get the current number of commands\n     * - Spade.mixins#stick( [ Boolean attach ] ) attach #info to every mix-in.\n     *\n     * See https://github.com/rootslab/syllabus#properties-methods.\n     */\n    semver : null\n\n    /*\n     * Hiboris option. For default, the loading\n     * of 'hiredis' native parser is disabled\n     * in favour of ( the slower ) Boris JS parser.\n     */\n    , hiredis : false\n\n    /*\n     * Cocker socket options\n     */\n    , socket : {\n        path : null\n        , address : {\n            // 'localhost'\n            host : '127.0.0.1'\n            , port : 6379\n            , family : null\n        }\n        , reconnection : {\n            trials : 3\n            , interval : 1000\n            /*\n             * A value to use for calculating the pause between two\n             * connection attempts. Default value is the golden ratio.\n             * Final value is calculated as:\n             * interval * Math.pow( factor, curr.attempts + 1 )\n             */\n            , factor : ( Math.sqrt( 5 ) + 1 ) / 2\n        }\n        , connection : {\n            /*\n             * encoding could be: 'ascii', 'utf8', 'utf16le' or \n             * 'ucs2','buffer'. It defaults to null or 'buffer'.\n             */\n            encoding : null\n            /*\n             * keepAlive defaults to true ( it is false in net.Socket ).\n             * Specify a number to set also the initialDelay.\n             */\n            , keepAlive : true\n            /*\n             * 'timeout' event delay, default is 0 ( no timeout ).\n             */\n            , timeout : 0\n            /*\n            * noDelay is true for default, it disables the Nagle\n            * algorithm ( no TCP data buffering for socket.write ).\n            */\n            , noDelay : true\n            /*\n             * If true, the socket won't automatically send a FIN\n             * packet when the other end of the socket sends a FIN\n             * packet. Defaults to false.\n             */\n            , allowHalfOpen : false\n        }\n    }\n    /*\n     * Security options.\n     *\n     * Options for db selection and password sending when the \n     * client connects to a particular host.\n     * An entry will be automatically added with the socket.address\n     * or socket.path defined in the constructor option. However,\n     * two sample entries are already present in the cache, holding\n     * default values from redis.conf. \n     *\n     * Every entry should be a file path ('/path/to/file.sock'),\n     * or a network path ('ip:port'), and should contain a:\n     *\n     * - 'requirepass' property, it contains the Redis password string\n     * for the current host. It defaults to null.\n     * Whenever a client connection is established and if an entry is\n     * found in the security hash. an AUTH command will be sent to Redis,\n     * before any other command in the command queue.\n     *\n     * - 'db' property, it defaults to 0. On every reconnection the first\n     * command to send after AUTH is SELECT db. If db === -1, on client\n     * reconnection the SELECT command will not been sent.\n     *\n     * NOTE: If the AUTH reply is erroneous, an 'authfailed' event will\n     * be emitted, then the client will be automatically disconnected to\n     * force re-AUTH on reconnection; it also happens if AUTH isn't required\n     * by Redis, but was sent by the client.\n     * If authorization is granted by Redis, an 'authorize' event will be\n     * emitted, then if the command queue is not empty, it will be processed.\n     */\n     , security : {\n        // a network path (ip:port)\n        '127.0.0.1:6379' : {\n            requirepass : null\n            , db : 0\n        }\n        // a unix domain socket path\n        , '/tmp/redis.sock' : {\n            requirepass : null\n            , db : 0\n        }\n    }\n    /*\n     * Command queue options.\n     */\n    , queue : {\n        /*\n         * Set the max size for the rollback queue.\n         * It defaults to 2^16, to disable set it to 0.\n         */\n        rollback : 64 * 1024\n        /*\n         * Log the last access time to the queue's head.\n         * It is disabled for default.\n         *\n         * NOTE: It is used to store the last time a reply was received. \n         */\n        , timestamps : false\n    }\n}\n```\n_[Back to ToC](#table-of-contents)_\n\n----------------------------------------------------------------------\n\n### Properties\n\n\u003e Don't mess with these properties!\n\n```javascript\n/*\n * A property that holds the initial config object.\n */\nSpade.options : Object\n\n/*\n * An Object that holds all Redis commands/methods mix-ins\n * from Syllabus. It is a shortcut for Spade.mixins.commands.\n * See https://github.com/rootslab/syllabus#syllabus-commands.\n */\nSpade.commands : Object\n\n/*\n * A flag to indicate if the connection to Redis Server\n * is currently active.\n */\nSpade.ready : Boolean\n\n/*\n * A flag to avoid initializing scripts cache multiple times,\n * when the client is offline ( multiple #initCache() calls ).\n */\nSpade.cacheready : Boolean\n\n/*\n * An Object that holds all scheduled tasks.\n * See Spade#initTasks method to load defaults entries like 'polling'.\n * See Spade.qq property to handle tasks.\n */\nSpade.tasks : Object\n\n/*\n * Some shortcuts to internal modules.\n */\n\n/*\n * Cocker module that inherits from net.Socket.\n */\nSpade.socket : Cocker\n\n/*\n * Parser module, it could be an instance of Hiboris, a module\n * wrapper for the hiredis native parser, or the Boris JS parser.\n */\nSpade.parser : Hiboris | Boris\n\n/*\n * Libra Queue Manager for Commands/Replies bindings.\n */\nSpade.queue : Libra\n\n/*\n * Property that contains all mix-ins for the current semantic\n * version specified. Spade default value is false, interpreted\n * as '*'.\n *\n * NOTE: Specifying a particular semver version enables develop\n * mode, some mix-ins will be added to get infos about commands.\n * See Syllabus module for some examples.\n */\nSpade.mixins : Syllabus\n\n/*\n * A property that holds LUA commands and Cache, a shortcut\n * for Spade.mixins.lua.\n * See https://github.com/rootslab/syllabus#properties-methods.\n */\nSpade.lua : Object\n\n/*\n * Current cache property, an instance of Camphora.\n *\n * NOTE: the LUA script cache remains hidden until you explicitly\n * call the #initCache method.\n */\nSpade.lua.cache : Camphora\n\n/*\n * Cucu module to handle tasks.\n * See https://github.com/rootslab/cucu\n */\nSpade.qq : Cucu\n\n/*\n * A property that holds iterators for commands like SCAN, HSCAN, SSCAN, ZSCAN,\n * loaded with Spade#loadIterators.\n */\nSpade.iterators : Object\n\n/*\n * Debug Properties\n */\n\n/*\n * Gerry module to handle events logging.\n * See https://github.com/rootslab/gerry\n */\nSpade.logger : Gerry\n\n```\n_[Back to ToC](#table-of-contents)_\n\n----------------------------------------------------------------------\n\n### Methods\n\n\u003e Arguments within [ ] are optional.\n\n#### cli\n\n__Enable event logging to console__.\n\n\u003e This method enables/logs some extra event for debugging/testing purpose:\n\u003e  - __reply__ for Redis replies.\n\u003e  - __scanqueue__ when the \"offline\" command queue is processed.\n\u003e  - __queued__ for commands executed when the client is offline.\n\n\u003e __NOTE__:\n\u003e  - the _'enable'_ option defaults to true.\n\u003e  - the _'logger'_ fn gets event name and event arguments.\n\n```javascript\nSpade#cli( [ Boolean enable [, Function logger [, Boolean collect_events ] ] ] ) : undefined\n```\n\u003e See \"[Other Debug Events](#other-debug-events)\" section.\n\n_[Back to ToC](#table-of-contents)_\n\n--------------------------------------------------------------------------------------------\n\n#### connect\n\n__Open a connection to the Redis Server__:\n\u003e  - When the connection is fully established, the __ready__ event will be emitted.\n\u003e  - You can optionally use a callback that will be executed on the __ready__ event.\n\u003e  - It accepts an optional socket confguration object.\n\u003e  - It returns the current Spade instance.\n\n\u003e __NOTE__: You don't need to listen for the __ready__ event, commands\n\u003e will be queued in _\"offline mode\"_ and written to socket when the\n\u003e connection will be ready.\n\n```javascript\n/*\n * socket_opt = {\n *      address : {\n *          host : '127.0.0.1'\n *          , port : 6379\n *      }\n *      , reconnection : {\n *          trials : 3\n *          , interval : 1000\n *      }\n *      , connection : {\n *          ...\n *      }\n *  }\n */\nSpade#connect( [ Object socket_opt [, Function cback ] ] ) : Spade\n```\n_[Back to ToC](#table-of-contents)_\n\n---------------------------------------------------------------------------------------\n\n#### disconnect\n\n__Disconnect client from the Redis Server__:\n\u003e  - You can optionally use a cback that will be executed after socket\n\u003e    disconnection.\n\u003e  - It returns the current Spade instance.\n\n\u003e __NOTE__: From the client point of view, executing disconnect has the same\n\u003e effect of sending and executing the Redis **_QUIT_** command. Connection will\n\u003e be closed and no other re-connection attempts will be made.\n\n```javascript\nSpade#disconnect( [ Function cback ] ) : Spade\n```\n_[Back to ToC](#table-of-contents)_\n\n---------------------------------------------------------------------------------------\n\n#### initCache\n\n__Initialize or reveal the (hidden) LUA script cache__:\n\u003e  - It loads and sends all the files found in the __./node_modules/syllabus/lib/lua/scripts__\n\u003e    directory, to the Redis Server, always after the __ready__ event.\n\u003e  - It triggers __cacheinit__, __cacheload__, __cacheready__ and __scriptfailure__ events.\n\u003e  - Optionally you could specify:\n\u003e    - a custom loading path with something like : _{ filepath : '/my/scripts/dir' }_.\n\u003e    - a custom init configuration for the Camphora costructor to (re)build the cache.\n\u003e    - a cback that will be executed on __cacheready__ passing the current cache instance\n\u003e      as argument.\n\n\u003e __NOTE__: Empty files and scripts, processed and then refused by Redis with an error reply,\n\u003e are automatically evicted from the cache.\n\n\u003e __NOTE__: If **_cache_opt_** is already set, the cache will be re-initialized; it happens\n\u003e only if the cache is ready, or when no other script commands are already been queued and\n\u003e not yet been sent to Redis (for example, when the client is offline); otherwise the cache\n\u003e will remain intact and an Error will be passed to the callback as the first argument.\n\n```javascript\n/*\n *  Default values for 'cache_opt' are:\n *  {\n *    capacity : 128\n *    , encrypt_keys : false\n *    , algorithm : 'sha1'\n *    , output_encoding : 'hex'\n *    , input_encoding : 'binary'\n *  }\n */\nSpade#initCache( [ Object f_opt [, Object cache_opt, [ Function cback ] ] ] ) : undefined\n```\n\u003e See [Camphora#load](https://github.com/rootslab/camphora#options) for a list of available\n\u003e options for cache.\n\n\u003e See \"[Script Cache Events](#script-cache-events)\" Section for the list of events.\n\n_[Back to ToC](#table-of-contents)_\n\n--------------------------------------------------------------------------------------------\n\n#### initTasks\n\n\u003e Load methods/tasks from __'spade/lib/tasks'__ directory.\n\u003e It returns the current _Spade.tasks_ property (or Cucu.ttable).\n\n```javascript\n// filenames should be without '.js' extension.\nSpade#initTasks( [ Array file_list ] ) : Object\n```\n\n##### polling task\n\n\u003e For default, the 'connection.js' file exports/adds a single polling method to tasks.\n\u003e When _polling_ is enabled, the client starts to __PING__ server every 60 secs; it could\n\u003e be useful for testing connection aliveness, when the client is in PubSub mode.\n\n\u003e Start the polling task:\n\n```javascript\nSpade.tasks.polling.run( [ Number interval [, Array polling_fn_args [, Number times ] ] ] ) : Number\n```\n\u003e the polling method could receive 4 optional arguments, through the __polling_fn_args__ Array:\n\n```javascript\npollingFn : function ( [ Function cback [, String ping_msg [, Number timeout [, Boolean reconnect ] ] ] ] )\n```\n\u003e for example:\n\n```javascript\n/*\n * Pinging server every 2 seconds, with a 'BANG!' message and stop after 10 times.\n * For every reply received client emits 'polling' event.\n * If no reply will be received within 1 sec, client emits the 'hangup' event, then\n * disconnects and reconnects to the server.\n */\n var client = require( 'spade' )()\n ...\n client.cli();\n client.connect();\n ...\n client.initTasks();\n\n client.tasks.polling.run( 2000, [ null, 'BANG!', 1000, true ], 10 );\n ...\n client.task.polling.stop();\n ..\n```\n\n\u003e __NOTE__:\n\u003e - executing #initTasks, automatically enables/adds the __polling__ and __hangup__ events.\n\u003e - when in __PubSub__ mode, the __rollback mechanism doesn't save PINGs__ sent by the\n\u003e   polling task.\n\n\u003e See [Tasks Events](#tasks-events).\n\n\u003e See [Cucu](https://github.com/rootslab/cucu) to see all available options to handle tasks.\n\n\u003e See [polling tests](test/).\n\n_[Back to ToC](#table-of-contents)_\n\n--------------------------------------------------------------------------------------------\n\n#### loadIterators\n\n\u003e Load default iterators, for commands like __SCAN__, __SSCAN__, __HSCAN__, __ZSCAN__,\n\u003e from _'spade/lib/iterators'_ dir, you could restrict files to load, specifying some\n\u003e filenames without '.js' extension, for example: [ 'scan' ].\n\n```javascript\nSpade#loadIterators( [ Array file_list ] ) : Object\n```\n\u003e It returns the current __Spade.iterators__ property filled with:\n\n```javascript\nSpade.iterators : {\n   scan : function ( Number cursor [, Object opt [, Function cback ] ] ) : Object\n   , hscan: function ( String key, Number cursor [, Object opt [, Function cback ] ] ) : Object\n   , sscan: function ( String key, Number cursor [, Object opt [, Function cback ] ] ) : Object\n   , zscan : function ( String key, Number cursor [, Object opt [, Function cback ] ] ) : Object\n}\n\n// you could use #next outside the callback to receive other results.\nObject : { next : Function }\n\n// default options for iterator commands:\nopt : { match : null, count : 10 }\n\n// 'cback' gets 3 arguments, you could call 'iterate' function inside the callback to receive next results.\ncback : function ( Boolean is_err_reply, Array reply, Function iterate )\n\n// reply Array for SCAN command:\nreply : [ Boolean is_last_iter, Array keys, Number iter_counter, Number keys_counter ]\n\n// for HSCAN, SSCAN, ZSCAN commands, the last element is the current hash/set/zset key being scanned.\nreply : [ Boolean is_last_iter, Array keys, Number iter_counter, Number keys_counter, String key ]\n\n```\n\u003e __NOTE__:\n\u003e  - signatures are the same as for relative __Spade.commands__, but they returns __iterator objects__.\n\u003e  - when console logging is enabled with __[Spade#cli](#cli)__, 3 additional __[debug events](#other-debug-events)__ will be added: \n\u003e    - *__scan__*\n\u003e    - *__sscan__*\n\u003e    - *__hscan__*\n\u003e    - *__zscan__*\n\n\u003e See [scan example](example/iterator-scan-example.js).\n\n\u003e See [iterators events](#iterators-events).\n\n_[Back to ToC](#table-of-contents)_\n\n---------------------------------------------------------------------------------------\n\n#### Redis Commands\n\n\u003e The __Spade.commands__ property contains all methods to encode and send __Redis__ commands,\n\u003e via the __Syllabus__ module.\n\n\u003e __Brief List of Redis Command Types:__\n\u003e - [__Cluster__](https://github.com/rootslab/syllabus#cluster) : _1 command (19 missing)_.\n\u003e - [__Connection__](https://github.com/rootslab/syllabus#connection) : _6 commands_.\n\u003e - [__Geo__](https://github.com/rootslab/syllabus#geo) : _6 commands_.\n\u003e - [__Hashes__](https://github.com/rootslab/syllabus#hashes) : _15 commands_.\n\u003e - [__HyperLogLog__](https://github.com/rootslab/syllabus#hyperloglog) : _4 commands_.\n\u003e - [__Keys__](https://github.com/rootslab/syllabus#keys) : _24 commands_.\n\u003e - [__Lists__](https://github.com/rootslab/syllabus#lists) : _17 commands_.\n\u003e - [__PubSub__](https://github.com/rootslab/syllabus#pubsub) : _6 commands_.\n\u003e - [__Scripting__](https://github.com/rootslab/syllabus#scripting) : _7 commands_.\n\u003e - [__Sets__](https://github.com/rootslab/syllabus#sets) : _15 commands_.\n\u003e - [__Server__](https://github.com/rootslab/syllabus#server) : _31 commands_.\n\u003e - [__Sorted Sets__](https://github.com/rootslab/syllabus#sorted-sets) : _21 commands_.\n\u003e - [__Strings__](https://github.com/rootslab/syllabus#strings) : _23 commands, (1 missing)_.\n\u003e - [__Transactions__](https://github.com/rootslab/syllabus#transactions) : _5 commands_.\n\n\u003e See **_[Syllabus Commands Section](https://github.com/rootslab/syllabus#syllabus-commands)_** for all signatures and available commands.\n\n#### Command Callback\n\n\u003e __Every command mix-in accepts a callback__ function as the last argument, it will get __3__ arguments:\n\n```javascript\n/*\n * 'is_err_reply' is a Boolean that signals an ERROR reply from Redis,\n * ( not a JS Error ), then reply data will contain the error message(s).\n *\n * 'data' is a list containing reply data Buffers ( or Strings if hiredis is used ).\n *\n * 'reveal' is a utility function that converts the raw Redis Reply in a simple\n * and usable form.\n *\n * NOTE: The utility function is not the same for all command replies, because,\n * as we surely know, some reply needs particular format and type conversions.\n */\n'callback' : function ( Boolean is_err_reply, Array data, Function reveal ) : undefined\n```\n\u003e __Example Code__:\n\n```javascript\nvar log = console.log\n    , Spade = require( 'spade' )\n    , client = Spade( {} )\n    ;\n\n// start async connection to Redis\nclient.connect();\n\n// execute TIME command\nclient.commands.time( function ( is_err_reply, reply_data_arr, reveal_fn ) {\n    log( '- error reply:', is_err_reply );\n    log( '- raw reply:', reply_data_arr );\n    log( '- converted reply:', reveal_fn( reply_data_arr ) );\n} );\n```\n_[Back to ToC](#table-of-contents)_\n\n#### Interactive Mode\n\n\u003e Specifying a __semver__ option ( like __*__ or __1.0.0__ ) enables _Syllabus_\n\u003e development mode for commands.\n\n__Example Code for REPL__\n\n```bash\n$ cd spade/\n$ node\n\n# you can directly load the interactive example file executing:\n# \u003e cd spade/example\n# \u003e .load repl-example.js\n\n# create a client instance with all commands (*)\n\u003e var client = require( './' )( { semver : '*' } )\n..\n# enable automatic console logging for events\n\u003e client.cli()\n..\n# get info about a command via mixins (Syllabus) property\n\u003e client.mixins.info('CoNfiG')\n\n{ req: 'CoNfiG',\n  name: 'config',\n  args: -1,\n  type: 'server',\n  cmd: 'CONFIG',\n  sub: \n   [ 'GET',\n     'RESETSTAT',\n     'REWRITE',\n     'SET' ] }\n\n# you can also stick the #info method to all available commands\n\u003e client.mixins.stick()\n\n..returns a number..\n\n# now get info about a method/command\n\u003e client.commands.ping.info()\n\n{ req: 'ping',\n  name: 'ping',\n  args: 0,\n  type: 'connection',\n  cmd: 'PING',\n  sub: [],\n  url: 'http://redis.io/commands/ping',\n  rtype: '+',\n  since: '1.0.0',\n  hint: 'PING',\n  descr: 'Ping the server.' }\n\n# now get info about a nested method/command\n\u003e client.commands.pubsub.numpat.info()\n\n{ req: 'pubsub numpat',\n  name: 'pubsub.numpat',\n  args: 0,\n  type: 'pubsub',\n  cmd: 'PUBSUB NUMPAT',\n  sub: [],\n  url: 'http://redis.io/commands/pubsub',\n  rtype: ':',\n  since: '2.8.0',\n  hint: 'PUBSUB NUMPAT',\n  descr: 'Returns the total number of patterns all the clients are subscribed to.' }\n\n# ok stop, now execute the command\n\u003e client.commands.pubsub.numpat()\n\n!queued: [ { bulks: 2,\n    cmd: 'PUBSUB',\n    ecmd: '*2\\r\\n$6\\r\\nPUBSUB\\r\\n$6\\r\\nNUMPAT\\r\\n',\n    fn: [Function],\n    zn: [Function] },\n  1 ]\n\n# ops, command is not sent but queued, then open client connection..\n\u003e client.connect()\n..\n# read your reply, then quit or disconnect\n\u003e client.commands.quit()\n..\n```\n\u003e See **_[Syllabus Properties and Methods Section](https://github.com/rootslab/syllabus#properties-methods)_** and _[repl-example.js](example/repl-example.js)_.\n\n_[Back to ToC](#table-of-contents)_\n\n#### LUA Cache and SCRIPT Methods\n\n\u003e Manually execute scripts commands, differently from _**Spade.commands.script**_ methods,\n\u003e _**Spade.lua.script**_ will also update the hidden cache for __LUA__ scripts.\n\n\u003e __NOTE__: these mix-ins are used internally by the **[initCache](#methods)** method ,\n\u003e for loading script files and for revealing the hidden __LUA__ cache.\n\n##### Spade.lua.script property\n\n```javascript\n/*\n * Get a script from the cache and send it to Redis.\n * It executes an EVALSHA command with the digest of the current script,\n * if it already exists.\n */\n#run( String name, Array keys, Array args [, Function cback ] ) : undefined\n \n/*\n * Manually load a script into the cache and send it to Redis.\n * It executes a SCRIPT LOAD command. 'lback' function will be called with an\n * argument that represents the entry loaded in the cache, or undefined if an\n * error occurs.\n */\n#load( String key, String data [, Function cback [, Function lback ] ] ) : undefined\n \n/*\n * Clear Spade and Redis cache. It executes a SCRIPT FLUSH command.\n * 'fback' function will be called with the number of elements flushed from the\n * cache.\n */\n#flush( [ Function cback [, Function fback ] ] ) : undefined\n```\n\u003e See **_[Syllabus.lua](https://github.com/rootslab/syllabus#properties-methods)_**.\n\n_[Back to ToC](#table-of-contents)_\n\n-----------------------------------------------------------------------------\n\n## Events\n\n- __[Events Sequence Diagram](#events-sequence-diagram)__\n- __[Error Events](#error-events)__\n- __[Auth Events](#auth-events)__\n- __[Select Events](#select-events)__\n- __[Script Cache Events](#script-cache-events)__\n- __[Socket Connection Events](#socket-connection-events)__\n- __[PubSub Events](#pubsub-events)__\n- __[Monitor Events](#monitor-events)__\n- __[Other Debug Events](#other-debug-events)__\n\n#### Events Sequence Diagram\n\n\u003e  - the event emitted for first could be:\n    - **_connect_** or **_offline_**, after the execution of __connect__ or __disconnect__ methods.\n    - **_cacheinit_**, after the execution of __initCache__ method.\n    - **_error_**, it _\"simply\"_ happens.\n\n```javascript\n                             +                                     +\n                             |                                     |\n                             v                                     v\n                          connect+-------\u003e(timeout)              error\n                             +\n                             |\n                             +--------\u003e(authorized)                +\n                             |               +                     |\n                             V               |                     v\n              +         (authfailed)   +-----+-----+          (cacheinit)\n              |              +         |           |               +\n              v              |         v           v               |\n  +-------\u003eoffline\u003c----+-----+----+(dbfailed) (dbselected)         |\n  |           +        |     |                     +               |\n  |           |        |     |                     |               |\n  +           v        |     |                     v               |\nlost\u003c----+(*attempt*)  |     +------------------+ready+------------+\n  +           +        |                           +               |\n  |           |        +--+(*monitor*)\u003c--+         |               v\n  |           |                          |         |      +--------+-------+\n  +-\u003econnect\u003c-+                          +---------+      |                |\n        +                                |                v                v\n        |                   (listen)\u003c----+        (*scriptfailure*)   (*cacheload*)\n        v                       +                         +                +\n       ...                      |                         |                |\n                                v                         +--------+-------+\n                           (*message*)+---\u003e(shutup)                |\n                                                                   v\n                                                              (cacheready)\n```\n\n\u003e __NOTE__:\n\u003e  - events between __(__ __)__ could never happen, most of them depends on client configuration.\n\u003e  - events within __*__ could be emitted more than once, namely __0__ or __k__ times with _k \u003e= 1_.\n\u003e  - __timeout__ could happen in _\"any\"_ moment after the __connect__ event.\n\u003e  - __listen__ signals that client is entering in subscription mode\n\u003e  - __shutup__ signals that client is (voluntarily) leaving subscription mode.\n\u003e  - __monitor__ mode could end only with a __QUIT__ command (then '_offline_').\n\n_[Back to ToC](#table-of-contents)_\n\n#### Error Events\n\n```javascript\n/*\n * A parser or command encoding error has occurred.\n */\n'error' : function ( Error err, Object command )\n```\n\n#### Auth Events\n\n\u003e These events are emitted on every client (re)connection to Redis and only if\n\u003e __AUTH is set to be mandatory__ for the current connected host; namely, should\n\u003e exist an entry, _'ip:port'_ or _'/path/to/file'_, in the options.security hash,\n\u003e with __requirepass__ property set to a non empty string.\n\n```javascript\n/*\n * The reply to AUTH command is an Error, then client will be disconnected;\n * it also happens when AUTH is not required by Redis but issued by the client.\n * No 'ready' event could be launched.\n */\n'authfailed' : function ( String password, Array reply, Object address )\n\n/*\n * Client authorization was successful. After that, the command queue will be\n * processed and the 'ready' event could be launched.\n */\n'authorized' : function ( String password, Array reply, Object address )\n```\n_[Back to ToC](#table-of-contents)_\n\n#### Select Events\n\n\u003e These events are emitted on every client (re)connection to __Redis__. If the __db__\n\u003e property is set ( \u003e== 0 ) in the options.security hash, for the current connected\n\u003e Redis host, the __SELECT__ db command will be sent before all the other commands\n\u003e already present in the queue.\n\u003e\n\u003e __NOTE__:\n\u003e When __Redis needs authentication__, __SELECT__ command will be sent __only after__\n\u003e having sent the __AUTH__ command and having received a successful reply from __Redis__.\n\n```javascript\n/*\n * The reply to SELECT command is an Error, then client will be disconnected.\n * No 'ready' event could be launched.\n */\n'dbfailed' : function ( String db, Array reply, Object address )\n\n/*\n * Client authorization is successful. Now the command queue will be processed,\n * the 'ready' event could be launched.\n */\n'dbselected' : function ( String db, Array reply, Object address )\n```\n_[Back to ToC](#table-of-contents)_\n\n#### Script Cache Events\n\n```javascript\n/*\n * Cache was initialized, script files are loaded in memory and a list\n * of SCRIPT LOAD commands are ready to be written to the socket.\n * Now that script commands are queued, it is possible to use and send\n * these and other commands.\n */\n'cacheinit' : function ( Array script_load_commands )\n\n/*\n * A script was processed and refused by Redis with an error reply.\n * Errored scripts are not added to local cache.\n */\n'scriptfailure' : function ( String script_name, String error_message )\n\n/*\n * A script was loaded in the cache and was successfully processed by Redis.\n */\n'cacheload' : function ( String script_name, Buffer data, String txt )\n\n/*\n * All scripts, found in the Syllabus scripts directory, are written to socket\n * and processed by Redis.\n *\n * NOTE: 'cacheready' event happens always after the 'ready' connection event,\n * because all scripts should be processed by Redis before launching this event.\n *\n * NOTE: cache for LUA scripts is an instance of Camphora module.\n */\n'cacheready' : function ( Camphora lua_script_cache )\n```\n_[Back to ToC](#table-of-contents)_\n\n#### Socket Connection Events\n\n```javascript\n/*\n * After that the client has established a connection to the Redis host,\n * it is now ready to write commands to the socket and to receive the\n * relative replies from Redis. It happens after processing AUTH and\n * SELECT commands and finally the offline queue.\n *\n * NOTE: Every commands executed by the client before the 'ready' event,\n * will be enqueued in 'offline mode' and sent/written to socket when\n * 'ready' will be emitted.\n */\n'ready' : function ( Object address, Number queued_command_sent )\n\n/*\n * A client connection is fully established with Redis host. This event\n * happens before 'ready' and AUTH/SELECT related events.\n */\n'connect' : function ( Object address )\n\n/*\n * Connection is currently down ( on the first 'close' event from the socket ).\n */\n'offline' : function ( Object address )\n\n/*\n * Client is trying to reconnect to Redis server, k is the number of current\n * connection attempt.\n *\n * NOTE: 'millis' indicates the last interval of time between attempts.\n */\n'attempt' : function ( Number k, Number millis, Object address )\n\n/*\n * The connection is definitively lost ( after opt.reconnection.trials times ).\n */\n'lost' : function ( Object address )\n\n/*\n * The socket times out for inactivity.\n * It only notifies that the socket has been idle.\n */\n'timeout' : function ( Number timeout,  Object address )\n```\n_[Back to ToC](#table-of-contents)_\n\n#### PubSub Events\n\n\u003e __NOTE__: for multiple (__P__)(__UN__)__SUBSCRIBE__ commands, __callbacks are executed\n\u003e one time for every message reply__, those __messages__ will be received also through the _Pub/Sub_\n\u003e system. However the first callback signals that the command is succesfully processed by Redis.\n\u003e\n\u003e For example:\n\u003e  - __subscribe( [ 'a', 'a', 'b', 'b', 'c', 'c' ], cback )__ :\n\u003e    - executes callback __6__ times\n\u003e    - produces __6__ messages\n\u003e    - __3__ actual subscriptions\n\u003e  - __unsubscribe( null, cback )__:\n\u003e    - executes cback __3__ times\n\u003e    - produces __3__ messages\n\u003e    - __0__ subscriptions.\n\n```javascript\n/*\n * A message was received through the PubSub system when the client\n * is in PubSub mode.\n *\n * NOTE: the 'formatter' function converts the received 'message' to\n * an obj/hash.\n * For example, a message reply to a (P)(UN)SUBSCRIBE command issued\n * by the client, could be:\n *\n * 'message' -\u003e [ 'unsubscribe', 'channel', 0 ]\n *\n * will be converted to:\n *\n * {\n *  type : 'unsubscribe'\n *  , chan : 'channel'\n *  . subs : 0\n * }\n *\n * a typical message received from publisher(s):\n *\n * 'message' -\u003e [ 'message', 'channel', 'Hello folks!' ]\n *\n * will be converted to:\n *\n * {\n *  type : 'message'\n *  , chan : 'channel'\n *  . msg : 'Hello folks!!'\n * }\n *\n * See also Syllabus.formatters.\n *\n */\n'message' : function ( Array message, Function formatter )\n\n/*\n * An event to signal that the client is entering in PubSub mode after a\n * subscription command. From now, all replies to (un)subscription commands\n * will be received as messages.\n */\n'listen' : function ()\n\n/*\n * An event to signal that client is leaving PubSub mode after a successfull\n * execution of a unsubscription command.\n * It doesn't happen if the client disconnects while in PubSub mode.\n */\n'shutup' : function ()\n```\n_[Back to ToC](#table-of-contents)_\n\n#### Monitor Events\n\n```javascript\n/*\n * A 'message' was received when the client is in Monitor mode.\n * \n * NOTE: the 'formatter' function converts the 'message' to an obj/hash.\n * For example, with an input like:\n *\n * message = '1405478664.248185 [0 127.0.0.1:47573] \"ping\"',\n *\n * executing formatter( message ) will output:\n *\n * {\n *  ip: '127.0.0.1',\n *  port: 47573,\n *  db: 0,\n *  cmd: '\"ping\"',\n *  utime: 1405478664248,\n *  time: [ 1405478664, 248185 ]\n * }\n *\n * See also Syllabus.formatters.\n *\n */\n'monitor' : function ( String message, Function formatter )\n```\n_[Back to ToC](#table-of-contents)_\n\n#### Tasks Events\n\n\u003e __NOTE__: to enable logging for events below, execute __Spade#initTasks__ method.\n\n##### polling events\n\n```javascript\n/*\n * polling event will be emitted every time a reply for PING will be received.\n */\n'polling' : function ( Number is_pubsub_active, Number is_monitor_active )\n\n/*\n * When the client doesn't receive a reply within the timeout interval specified\n * with Spade.tasks.polling#run, it disconnects from server, and optionally\n * reconnects.\n */\n'hangup' : function ( Number is_pubsub_active, Number is_monitor_active )\n```\n\n\u003e See __[polling task](#polling-task)__.\n\n#### Other Debug Events\n\n\u003e __NOTE__: to enable logging for debug events , execute __[Spade#cli](#cli)__ method.\n\n```javascript\n/*\n * When the client is offline, commands are not sent but queued.\n */\n'queued' : function ( Object command, Number offline_queue_size )\n\n/*\n * When the client will be online once again, this event is emitted\n * before performing a scan for sending enqueued commands, then always\n * before the 'ready' event.\n */\n'scanqueue' : function ( Number offline_queue_size )\n\n/*\n * The client receives a command reply from Redis. It always happens after\n * the 'ready' event.\n */\n'reply' : function ( Object command, String reply )\n\n/*\n * The client receives an error reply from Redis. It always happens after\n * the 'ready' event.\n */\n'error-reply' : function ( Object command, String err_reply )\n```\n\n##### iterators events\n\n\u003e __NOTE__: to enable logging for events below, execute also __Spade#loadIterators__ method.\n\n```javascript\n'scan' : function ( Boolean is_last_iter, Number iterations, Number keys_counter )\n'hscan' : function ( Boolean is_last_iter, Number iterations, Number keys_counter, String key )\n'sscan' : function ( Boolean is_last_iter, Number iterations, Number keys_counter, String key )\n'zscan' : function ( Boolean is_last_iter, Number iterations, Number keys_counter, String key )\n```\n\u003e See __[#loadIterators](#loaditerators)__.\n\n_[Back to ToC](#table-of-contents)_\n\n-------------------------------------------------------------------------------\n\n### MIT License\n\n\u003e Copyright (c) 2014-present \u0026lt; Guglielmo Ferri : 44gatti@gmail.com \u0026gt;\n\n\u003e Permission is hereby granted, free of charge, to any person obtaining\n\u003e a copy of this software and associated documentation files (the\n\u003e 'Software'), to deal in the Software without restriction, including\n\u003e without limitation the rights to use, copy, modify, merge, publish,\n\u003e distribute, sublicense, and/or sell copies of the Software, and to\n\u003e permit persons to whom the Software is furnished to do so, subject to\n\u003e the following conditions:\n\n\u003e __The above copyright notice and this permission notice shall be\n\u003e included in all copies or substantial portions of the Software.__\n\n\u003e THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\n\u003e EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n\u003e MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n\u003e IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n\u003e CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n\u003e TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n\u003e SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frootslab%2Fspade","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frootslab%2Fspade","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frootslab%2Fspade/lists"}