{"_id":"@dfroehli42/infinispan","_rev":"1-2b38921f37339068fb6dd77e6bfe8f3e","name":"@dfroehli42/infinispan","dist-tags":{"latest":"0.7.0-hrjs36-basic-auth"},"versions":{"0.7.0-hrjs36-basic-auth":{"name":"@dfroehli42/infinispan","version":"0.7.0-hrjs36-basic-auth","description":"Infinispan Javascript client","main":"index","directories":{"lib":"lib"},"scripts":{"test":"jasmine-node spec"},"author":{"name":"Galder Zamarreño"},"license":"Apache-2.0","keywords":["infinispan","cache","client","cluster","failover","hashing","nosql"],"repository":{"type":"git","url":"git+https://github.com/infinispan/js-client.git"},"dependencies":{"bitwise-xor":"0.0.0","create-hash":"^1.1.0","create-hmac":"^1.1.3","log4js":"^3.0.5","promise.prototype.finally":"^3.1.0","randombytes":"^2.0.1","sasl-digest-md5":"^0.1.0","saslmechanisms":"^0.1.1","underscore":"^1.9.1"},"devDependencies":{"growl":"^1.10.5","heapdump":"^0.3.9","jasmine-node":"^1.16.2","jsdoc":"^3.6.6","request":"^2.88.0"},"bugs":{"url":"https://github.com/infinispan/js-client/issues"},"homepage":"https://github.com/infinispan/js-client#readme","gitHead":"6659686a2578a3b1601b53aa0bb72cfe5d80f2b1","_id":"@dfroehli42/infinispan@0.7.0-hrjs36-basic-auth","_nodeVersion":"10.16.0","_npmVersion":"7.6.1","dist":{"integrity":"sha512-JCyKNu+b4WMICzYRYZbr9K9bcpQN46V49haHcfHyUHnkVq0bii2klEjadTpQgDThMi5cmVmY8tchk/L/1n6X1g==","shasum":"f1e08676dc5d8570e361bf548cdf0f30d42c5f92","tarball":"https://registry.npmjs.org/@dfroehli42/infinispan/-/infinispan-0.7.0-hrjs36-basic-auth.tgz","fileCount":59,"unpackedSize":380861,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTO2XCRA9TVsSAnZWagAAYSUP/R3dUnw1PjyyzWfM7wR5\nnsSr3sVCG8gf0JeYZE9aVwt8a8lJU/i9yRAEsKx3I0w0rrd5cAw9qAlofk2L\n22IIYMDgGxVhHmP2Wg9wk9WCs4/jFQR05+Rmzcc9My1bM3aYYMd8wGvcfeWv\n1iLt7LSdukDf7zfi35igxAy24ox8WHAAFQ0AgXvAgwgFQisgfoPNEjCqXodg\nyHl/v914+oXsfWbTAhKTrMB1OC3HkkWcQdBxibuLiVs5uJ7yWCbrS5YySR4s\ndDGz+P+jNB8mBXXu4/B19TyPiM1iVWBCfsKoiBBQfb7aXt6RWCJuEHjDLNNv\nEFkXZu6U6XTJHKyUIp5H7l3bgyUoUB6tyJCKR6aa0x5UfqngV0SIeQfbXPUW\nK8IwJXjKK5RYYDCsodLTpdkHnI+MiKx9ZysNWdPUse3b2goFXqWBhovwWDcp\nm0qDFiDPK2zq5bJJysNoUYJ/htyZh+w6TSIi968PR5THSOuU0DN4bJbK4lhu\nTrffwU1b/loEdJFTNzANgP2unXLrgfMeQ9f4Ym5jCjYH53mh6Z3NqSXm1K3d\n/TJtaEd2EPEfcMwFyUU7DUlY6ltB+io10uQsIbHS3T5sNwIhar42mAh8iznW\nrBhEOnGWpAL1oEG/Y48EK+xmi7ELiFhcXnCfo9f3vQdk2RuHCkPM6xRlxDp+\nLlym\r\n=4edz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCSgLP7VkytBgnCtZNFrsz47zQBc9iq/HuGWwYu7iR0BwIhAM+jdvYdRxfQ8cRRENt6hpV5NTsms+SwrVTxy6CywdFV"}]},"_npmUser":{"name":"dfroehli42","email":"daniel.froehlich@web.de"},"maintainers":[{"name":"dfroehli42","email":"daniel.froehlich@web.de"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infinispan_0.7.0-hrjs36-basic-auth_1615654294460_0.20322929560264513"},"_hasShrinkwrap":false}},"time":{"created":"2021-03-13T16:51:34.422Z","0.7.0-hrjs36-basic-auth":"2021-03-13T16:51:34.662Z","modified":"2022-04-05T04:17:48.865Z"},"maintainers":[{"name":"dfroehli42","email":"daniel.froehlich@web.de"}],"description":"Infinispan Javascript client","homepage":"https://github.com/infinispan/js-client#readme","keywords":["infinispan","cache","client","cluster","failover","hashing","nosql"],"repository":{"type":"git","url":"git+https://github.com/infinispan/js-client.git"},"author":{"name":"Galder Zamarreño"},"bugs":{"url":"https://github.com/infinispan/js-client/issues"},"license":"Apache-2.0","readme":"# Infinispan JS Client\n\n`infinispan` is an asynchronous event-driven Infinispan client for Node.js.\nThe results of the asynchronous operations are represented using\n[Promise](https://www.promisejs.org) instances. Amongst many advantages,\npromises make it easy to transform/chain multiple asynchronous invocations\nand they improve error handling by making it easy to centralise it.\n\nThe client is under heavy development but here's a summary of its\ncurrent capabilities:\n\n* `infinispan` client can be constructed with a single server address or\nmultiple servers addresses. When passing multiple addresses, it will iterate\nuntil it finds a server to which it can connect to.\n* Clients can interact with a named cache whose name is passed on client\nconstruction via `{cacheName: 'myCache'}` option. In the absence of any cache\nname options, the client will interact with the default cache.\n* Full CRUD operation support, e.g. `put`, `get`, `remove`, `containsKey`...etc.\n* Compare-And-Swap operation support, e.g. `putIfAbsent`,\n`getWithMetadata`, `replace`, `replaceWithVersion`,\n`removeWithVersion`..etc.\n* Expiration with absolute lifespan or relative maximum idle time\nis supported. This expiration parameters as passed as optional parameters\nto create/update methods and they support multiple time units, e.g.\n`{lifespan: '1m', maxIdle: '1d'}`.\n* Update and remove operations can optionally return previous values\nby passing in `{previous: true}` option.\n* Bulk store/retrieve/delete operations are supported, e.g. `putAll`, `getAll`,\n`clear`...etc.\n* Cache contents can be iterated over using the `iterator` method.\n* Cache size can be determined using the `size` method.\n* Remote cache listeners can be plugged using the `addListener` method, which\ntakes the event type (`create`, `modify`, `remove` or `expiry`) and the\nfunction callback as parameter.\n* Clients can store scripts using `addScript` and then they can be remotely\nexecuted using the `execute` operation. Executing a script remotely\noptionally takes per-invocation parameters.\n* Server-side statistics can be retrieved using the `stats` operation.\n* Clients can connect using encryption with the server via SSL/TLS with optional TLS/SNI support.\n* Clients can talk to clusters of Infinispan Server instances, using\nConsistent-Hash based algorithms to route key-based operations.\n* Multi-key or key-less operations are routed in round-robin fashion.\n* Clients only need to be configure with a single node's address and from\nthat node the rest of the cluster topology can be discovered. As nodes are\nadded or destroyed, clients get notified of changes in the cluster topology\ndynamically.\n* Clients can talk to multiple clusters that are separated into different site clusters.\nThe client is normally connected to one of the sites, but if its members fail to respond, it will automatically switch to an alternative site to which it can connect.\n* Clients have methods, such as `switchToCluster(clusterName)` and `switchToDefaultCluster` that allows users to manually change to which site cluster to connect.\n* Finally, clients can stop communication with the server(s) using the\n`disconnect` method.\n\n# Requirements\n\n`infinispan` client requires Node.js version `8.11.4` or higher.\n\nIt can only talk to Infinispan Server 8.x or higher versions.\n\nBy default, Infinispan clients talk Hot Rod protocol version `2.9` which is\nsupported starting with Infinispan server 9.4.x.\n\nPlease find below information on how to use the client with older Infinispan server versions:\n\n*  For versions between `8.2.x` and `9.3.x`, use Hot Rod protocol version `2.5`.\nTo do so, construct the client with `{version: '2.5'}` optional argument.\n*  For versions `8.0.x` and `8.1.x`, use Hot Rod protocol version `2.2`.\nTo do so, construct the client with `{version: '2.2'}` optional argument.\n\n# API docs\n\nAPI documentation for the client can be found\n[here](http://docs.jboss.org/infinispan/hotrod-clients/javascript/1.0/apidocs/module-infinispan.html),\nwhere you can find detailed information of the APIs exposed.\n\n# Usage\n\nBefore executing these code samples, Infinispan Server must be downloaded\nfrom [here](http://infinispan.org/download/) and installed locally bearing\nin the support version information provided above. Unless indicated\notherwise, the code samples below require an Infinispan Server instance\nto be started. The simplest way to do so is to execute the following script:\n\n    $ [INFINISPAN_SERVER_HOME]/bin/server.sh\n\nPlease find below samples codes showing how the Infinispan Javascript client\ncan be used:\n\n## Working with single entries and statistics\n\n```Javascript\nvar infinispan = require('infinispan');\n\nvar connected = infinispan.client({port: 11222, host: '127.0.0.1'});\n\nconnected.then(function (client) {\n\n  var clientPut = client.put('key', 'value');\n\n  var clientGet = clientPut.then(\n      function() { return client.get('key'); });\n\n  var showGet = clientGet.then(\n      function(value) { console.log('get(key)=' + value); });\n\n  var clientRemove = showGet.then(\n      function() { return client.remove('key'); });\n\n  var showRemove = clientRemove.then(\n      function(success) { console.log('remove(key)=' + success); });\n\n  var clientStats = showRemove.then(\n    function() { return client.stats(); });\n\n  var showStats = clientStats.then(\n    function(stats) {\n      console.log('Number of stores: ' + stats.stores);\n      console.log('Number of cache hits: ' + stats.hits);\n      console.log('All stats: ' + JSON.stringify(stats, null, \" \"));\n    });\n\n  return showStats.finally(\n      function() { return client.disconnect(); });\n\n}).catch(function(error) {\n\n  console.log(\"Got error: \" + error.message);\n\n});\n```\n## Authentication\nThe client supports PLAIN authentication. Other authentication mechanisms will be supporded\nin the next releases.\n\n\n```Javascript\nvar connected = infinispan.client({port: 11222, host: '127.0.0.1'},\n{\n    authentication: {\n      enabled: true,\n      serverName: 'infinispan',\n      saslMechanism: 'PLAIN',\n      userName: 'admin',\n      password: 'pass'\n    }\n  }\n);\n```\n\n## Using conditional operations\n\n```Javascript\nvar infinispan = require('infinispan');\n\nvar connected = infinispan.client({port: 11222, host: '127.0.0.1'});\n\nconnected.then(function (client) {\n\n  var clientPut = client.putIfAbsent('cond', 'v0');\n\n  var showPut = clientPut.then(\n      function(success) { console.log(':putIfAbsent(cond)=' + success); });\n\n  var clientReplace = showPut.then(\n      function() { return client.replace('cond', 'v1'); } );\n\n  var showReplace = clientReplace.then(\n      function(success) { console.log('replace(cond)=' + success); });\n\n  var clientGetMetaForReplace = showReplace.then(\n      function() { return client.getWithMetadata('cond'); });\n\n  var clientReplaceWithVersion = clientGetMetaForReplace.then(\n      function(entry) {\n        console.log('getWithMetadata(cond)=' + JSON.stringify(entry));\n        return client.replaceWithVersion('cond', 'v2', entry.version);\n      }\n  );\n\n  var showReplaceWithVersion = clientReplaceWithVersion.then(\n      function(success) { console.log('replaceWithVersion(cond)=' + success); });\n\n  var clientGetMetaForRemove = showReplaceWithVersion.then(\n      function() { return client.getWithMetadata('cond'); });\n\n  var clientRemoveWithVersion = clientGetMetaForRemove.then(\n      function(entry) {\n        console.log('getWithMetadata(cond)=' + JSON.stringify(entry));\n        return client.removeWithVersion('cond', entry.version);\n      }\n  );\n\n  var showRemoveWithVersion = clientRemoveWithVersion.then(\n      function(success) { console.log('removeWithVersion(cond)=' + success)});\n\n  return showRemoveWithVersion.finally(\n      function() { return client.disconnect(); });\n\n}).catch(function(error) {\n\n  console.log(\"Got error: \" + error.message);\n\n});\n```\n\n## Working with multiple entries\n\n```Javascript\nvar infinispan = require('infinispan');\n\nvar connected = infinispan.client({port: 11222, host: '127.0.0.1'});\n\nconnected.then(function (client) {\n  var data = [\n    {key: 'multi1', value: 'v1'},\n    {key: 'multi2', value: 'v2'},\n    {key: 'multi3', value: 'v3'}];\n\n  var clientPutAll = client.putAll(data);\n\n  var clientGetAll = clientPutAll.then(\n    function() { return client.getAll(['multi2', 'multi3']); });\n\n  var showGetAll = clientGetAll.then(\n    function(entries) {\n      console.log('getAll(multi2, multi3)=%s', JSON.stringify(entries));\n    }\n  );\n\n  var clientIterator = showGetAll.then(\n    function() { return client.iterator(1); });\n\n  var showIterated = clientIterator.then(\n    function(it) {\n      function loop(promise, fn) {\n        // Simple recursive loop over iterator's next() call\n        return promise.then(fn).then(function (entry) {\n          return entry.done\n            ? it.close().then(function () { return entry.value; })\n            : loop(it.next(), fn);\n        });\n      }\n\n      return loop(it.next(), function (entry) {\n        console.log('iterator.next()=' + JSON.stringify(entry));\n        return entry;\n      });\n    }\n  );\n\n  var clientClear = showIterated.then(\n    function() { return client.clear(); });\n\n  return clientClear.finally(\n    function() { return client.disconnect(); });\n\n}).catch(function(error) {\n\n  console.log(\"Got error: \" + error.message);\n\n});\n```\n\n## Working with ephemeral data\n\n```Javascript\nvar infinispan = require('infinispan');\n\nvar connected = infinispan.client({port: 11222, host: '127.0.0.1'});\n\nconnected.then(function (client) {\n\n  var clientPutExpiry = client.put('expiry', 'value', {lifespan: '1s'});\n\n  var clientGetMetaAndSize = clientPutExpiry.then(\n    function() {\n      // Compute getWithMetadata and size in parallel\n      return Promise.all([client.getWithMetadata('expiry'), client.size()]);\n    });\n\n  var showGetMetaAndSize = clientGetMetaAndSize.then(\n    function(values) {\n      console.log('before expiration:');\n      console.log('getWithMetadata(expiry)=' + JSON.stringify(values[0]));\n      console.log('size=' + values[1]);\n    });\n\n  var clientContainsAndSize = showGetMetaAndSize.then(\n    function() {\n      sleepFor(1100); // Sleep to force expiration\n      return Promise.all([client.containsKey('expiry'), client.size()]);\n    });\n\n  var showContainsAndSize = clientContainsAndSize.then(\n    function(values) {\n      console.log('after expiration:');\n      console.log('containsKey(expiry)=' + values[0]);\n      console.log('size=' + values[1]);\n    });\n\n  return showContainsAndSize.finally(\n    function() { return client.disconnect(); });\n\n}).catch(function(error) {\n\n  console.log(\"Got error: \" + error.message);\n\n});\n\nfunction sleepFor(sleepDuration){\n  var now = new Date().getTime();\n  while(new Date().getTime() < now + sleepDuration){ /* do nothing */ }\n}\n```\n\n## Interact with named caches\n\n```Javascript\nvar infinispan = require('infinispan');\n\nvar connected = infinispan.client(\n  {port: 11222, host: '127.0.0.1'}, {cacheName: 'namedCache'});\n\nconnected.then(function (client) {\n\n  console.log('Connected to `namedCache`');\n\n  return client.disconnect();\n\n}).catch(function(error) {\n\n  console.log(\"Got error: \" + error.message);\n\n});\n```\n\n## Connect failover\n\nThe client can be configured with multiple server addresses and it will loop\nthrough them until it finds a node to which it can be connected, as shown\nin this example:\n\n```Javascript\nvar infinispan = require('infinispan');\n\n// Accepts multiple addresses and fails over if connection not possible\nvar connected = infinispan.client(\n  [{port: 99999, host: '127.0.0.1'}, {port: 11222, host: '127.0.0.1'}]);\n\nconnected.then(function (client) {\n\n  var members = client.getTopologyInfo().getMembers();\n\n  console.log('Connected to: ' + JSON.stringify(members));\n\n  return client.disconnect();\n\n}).catch(function(error) {\n\n  console.log(\"Got error: \" + error.message);\n\n});\n```\n\n# Supported data types\n\nBefore version 0.6, Infinispan Javascript client only supported String keys and values.\nStarting at version 0.6, the client also supports native JSON objects as keys and values.\n\n**NOTE**: This feature requires Infinispan server 9.4 or higher.\n\nThe way parameters are treated, whether String or native JSON objects, is defined by client configuration.\nFor backwards compatibility reasons, by default keys and values are treated as String values.\n\nSo, if using native JSON objects, it is necessary to adjust the client configuration:\n\n```Javascript\nvar infinispan = require('infinispan');\n\nvar connected = infinispan.client(\n    {port: 11222, host: '127.0.0.1'}\n    , {\n        dataFormat : {\n            keyType: 'application/json',\n            valueType: 'application/json'\n        }\n    }\n);\n\nconnected.then(function (client) {\n\n  var clientPut = client.put({k: 'key'}, {v: 'value'});\n\n  var clientGet = clientPut.then(\n      function() { return client.get({k: 'key'}); });\n\n  var showGet = clientGet.then(\n      function(value) { console.log(\"get({k: 'key'})=\" + JSON.stringify(value)); });\n\n  return showGet.finally(\n      function() { return client.disconnect(); });\n\n}).catch(function(error) {\n\n  console.log(\"Got error: \" + error.message);\n\n});\n```\n\nKey and value data types can be configured independently.\nHence, it's possible to have String keys and native JSON values or viceversa.\n\nCurrently all operations support native JSON objects except scripts.\nThey still rely on String key/value pairs and String parameters.\nSupport for native JSON objects in scripts will come at a later time.\n\n## Remote events\n\nClients can register event listeners that get invoked when data changes happen.\nCreate, modified, remove and expired events are supported.\nCreate and modified events emit key and version of value after event.\nRemove and expired events only emit key information.\n\n```Javascript\nvar infinispan = require('infinispan');\n\nvar connected = infinispan.client({port: 11222, host: '127.0.0.1'});\n\nconnected.then(function (client) {\n\n    var clientAddListenerCreate = client.addListener('create', onCreate);\n\n    var clientAddListeners = clientAddListenerCreate.then(\n        function(listenerId) {\n            // Multiple callbacks can be associated with a single client-side listener.\n            // This is achieved by registering listeners with the same listener id\n            // as shown in the example below.\n            var clientAddListenerModify =\n                client.addListener('modify', onModify, {listenerId: listenerId});\n\n            var clientAddListenerRemove =\n                client.addListener('remove', onRemove, {listenerId: listenerId});\n\n            return Promise.all([clientAddListenerModify, clientAddListenerRemove]);\n        });\n\n    var clientCreate = clientAddListeners.then(\n        function() { return client.putIfAbsent('eventful', 'v0'); });\n\n    var clientModify = clientCreate.then(\n        function() { return client.replace('eventful', 'v1'); });\n\n    var clientRemove = clientModify.then(\n        function() { return client.remove('eventful'); });\n\n    var clientRemoveListener =\n        Promise.all([clientAddListenerCreate, clientRemove]).then(\n            function(values) {\n                var listenerId = values[0];\n                return client.removeListener(listenerId);\n            });\n\n    return clientRemoveListener.finally(\n        function() { return client.disconnect(); });\n\n}).catch(function(error) {\n\n    console.log(\"Got error: \" + error.message);\n\n});\n\nfunction onCreate(key, version) {\n    console.log('[Event] Created key: ' + key +\n        ' with version: ' + JSON.stringify(version));\n}\n\nfunction onModify(key, version) {\n    console.log('[Event] Modified key: ' + key +\n        ', version after update: ' + JSON.stringify(version));\n}\n\nfunction onRemove(key) {\n    console.log('[Event] Removed key: ' + key);\n}\n```\n\nStarting with client version `0.7` and Infinispan `9.4.x` server,\nit's possible to tailor events to add or remove information from each event.\n\nFor example, a user might want to find out the value associated with the key after an event.\nIf the value was sent back along with the key within the event, unnecessary round trips to fetch the value would be avoided.\nThis can be achieved configuring the listener with a remote event converter.\n\nInfinispan servers come with a converter called `key-value-with-previous-converter-factory` which can be used for this purpose:\n\n```Javascript\nvar infinispan = require('infinispan');\n\nvar connected = infinispan.client(\n    {port: 11222, host: '127.0.0.1'}\n    , {\n        dataFormat : {\n            keyType: 'application/json',\n            valueType: 'application/json'\n        }\n    }\n);\n\nconnected.then(function (client) {\n\n    var opts = {\n        converterFactory : {\n            name: \"key-value-with-previous-converter-factory\"\n        }\n    };\n\n    var clientAddListenerCreate = client.addListener('create', logEvent(\"Created\"), opts);\n\n    var clientAddListeners = clientAddListenerCreate.then(\n        function(listenerId) {\n            // Multiple callbacks can be associated with a single client-side listener.\n            // This is achieved by registering listeners with the same listener id\n            // as shown in the example below.\n            var clientAddListenerModify =\n                client.addListener('modify', logEvent(\"Modified\"), {opts, listenerId: listenerId});\n\n            var clientAddListenerRemove =\n                client.addListener('remove', logEvent(\"Removed\"), {opts, listenerId: listenerId});\n\n            return Promise.all([clientAddListenerModify, clientAddListenerRemove]);\n        });\n\n    var clientCreate = clientAddListeners.then(\n        function() { return client.putIfAbsent('converted', 'v0'); });\n\n    var clientModify = clientCreate.then(\n        function() { return client.replace('converted', 'v1'); });\n\n    var clientRemove = clientModify.then(\n        function() { return client.remove('converted'); });\n\n    var clientRemoveListener =\n        Promise.all([clientAddListenerCreate, clientRemove]).then(\n            function(values) {\n                var listenerId = values[0];\n                return client.removeListener(listenerId);\n            });\n\n    return clientRemoveListener.finally(\n        function() { return client.disconnect(); });\n\n}).catch(function(error) {\n\n    console.log(\"Got error: \" + error.message);\n\n});\n\nfunction logEvent(prefix) {\n    return function(event) {\n        console.log(prefix + \" key: \" + event.key);\n        console.log(prefix + \" value: \" + event.value);\n        console.log(prefix + \" previous value: \" + event.prev);\n    }\n}\n```\n\nYou can also create and deploy your own converters into Infinispan server instances.\nSee the\n[event filter and conversion](https://infinispan.org/docs/stable/titles/developing/developing.html#event_filtering_and_conversion)\nsection in the Developers Guide for more information.\n\nNote that you must also configure encoding for cache definitions on Infinispan servers. Configure caches to use a MediaType that matches the data format for keys and values.\n\nSee the [configuring MediaType](https://infinispan.org/docs/dev/titles/developing/developing.html#encoding_media_type) section in the Developers Guide for more information.\n\n## Script Execution\n\nThe client has the ability to remotely execute scripts on the server.\nTo do so, it must first load the script in the server and then invoke it.\nSo, given the following script called `sample-script.js`:\n\n```Javascript\n// mode=local,language=javascript,parameters=[k, v],datatype='text/plain; charset=utf-8'\ncache.put(k, v);\ncache.get(k);\n```\n\nThe Infinispan Javascript client could load and execute it using the\nfollowing code:\n\n```Javascript\nvar infinispan = require('infinispan');\nvar readFile = Promise.denodeify(require('fs').readFile);\n\nvar connected = infinispan.client({port: 11222, host: '127.0.0.1'});\n\nconnected.then(function (client) {\n\n  var addScriptFile = readFile('sample-script.js').then(\n    function(file) {\n      return client.addScript('sample-script', file.toString());\n    });\n\n  var clientExecute = addScriptFile.then(\n    function() {\n      return client.execute('sample-script', {k: 'exec-key', v: 'exec-value'});\n    });\n\n  var showExecute = clientExecute.then(\n    function(ret) { console.log('Script execution returned: ' + ret); });\n\n  return showExecute.finally(\n    function() { return client.disconnect(); });\n\n}).catch(function(error) {\n\n  console.log(\"Got error: \" + error.message);\n\n});\n```\n\n## Encryption\n\nThe client supports encryption via SSL/TLS with optional TLS/SNI support ([Server Name Indication](https://en.wikipedia.org/wiki/Server_Name_Indication)).\nTo set this up, it is necessary to create a [Java KeyStore (JKS)](https://en.wikipedia.org/wiki/Keystore) using the `keytool` application which is part of the JDK.\nThe keystore needs to contains the keys and certificates necessary for the Infinispan Server to authorize connections.\nMore information on how to configure the Infinispan Server for encryption, along with TLS/SNI, can be found [here](https://infinispan.org/docs/stable/titles/server/server.html#security).\n\nIn the most basic set up, the Javascript client can be configured with the location of the trusted certificates so that the client connection is authorized by the server.\nThis assumes that the server has been configured with the correct certificates as stated above.\nWith that in mind, the client can be configured in the following way:\n\n```Javascript\nvar connected = infinispan.client({port: 11222, host: '127.0.0.1'},\n  {\n    ssl: {\n      enabled: true,\n      trustCerts: ['my-root-ca.crt.pem']\n    }\n  }\n);\n```\n\nAlternatively, the client can also read trusted certificates from `PKCS#12` or `PFX` format key stores:\n\n```Javascript\nvar connected = infinispan.client({port: 11222, host: '127.0.0.1'},\n  {\n    ssl: {\n      enabled: true,\n      cryptoStore: {\n        path: 'my-truststore.p12',\n        passphrase: 'secret'\n      }\n    }\n  }\n);\n```\n\nThe client can also be configured with encrypted authentication.\nTo do that, it's necessary to provide the location of the private key, the passphrase and certificate key of the client:\n\n```Javascript\nvar connected = infinispan.client({port: 11222, host: '127.0.0.1'},\n  {\n     {\n      enabled: true,\n      trustCerts: ['my-root-ca.crt.pem'],\n      clientAuth: {\n        key: 'privkey.pem',\n        passphrase: 'secret',\n        cert:ssl 'cert.pem'\n      }\n    }\n  }\n);\n```\n\nOptionally, the client can indicate which hostname it is attempting to connect to at the start of the TLS/SNI handshaking process:\n\n```Javascript\nvar connected = infinispan.client({port: 11222, host: '127.0.0.1'},\n  {\n    ssl: {\n      enabled: true,\n      trustCerts: ['my-root-ca.crt.pem']\n      sniHostName: 'example.com'\n    }\n  }\n);\n```\n\nIf no `sniHostName` is provided, the underlying Node.js TLS/SNI implementation sends `localhost` as SNI parameter.\nThis is important to note because if the server's default realm does not match `localhost`, you'll encounter errors such as `Hostname/IP doesn't match certificate's altnames`.\n\nAnother gotcha with the Node.js TLS/SSL implementation is that by default it does not allow self-signed certificates.\nIf using self-signed certificates, you'll encounter errors such as `DEPTH_ZERO_SELF_SIGNED_CERT` or `SSL certificate problem: Invalid certificate chain`.\nTo avoid problems like this in testing scenarios, one possible solution is to create your own certificate authority, which is used to sign all keys.\nAn example on how to do this can be found in the `make-root-ca-and-certificates.sh` script found in the root of this repository.\nThis script contains all the commands necessary to create your own CA, sign certificates, create private keys, and even create Java KeyStore files for the server.\nA more detailed example of the contents of this script can be found in [this repository](https://github.com/Daplie/nodejs-self-signed-certificate-example).\nAnother possibility is to get certificates from free, open certificate authorities such as [Let's Encrypt](https://letsencrypt.org).\n\n## Working with Clusters\n\nAll previous examples are focused on how the API behaves when working with a\nsingle Infinispan Server instance. Additionally, multiple Infinispan Servers\ncan be clustered in order to provide failover for the data and scale up.\nWorking with a Infinispan Server cluster is very similar to working with a\nsingle instance but there's a few things to bear in mind:\n\n* No matter the size of the Infinispan Server cluster, the client only needs\nto know about a server's address in order to get information about the entire\ncluster topology.\n* For distributed caches, key-based operations are routed in the cluster\nusing the same consistent hash algorithms used by the server, so that means\nthat the client can locate where a particular key resides without the need\nof extra network hops.\n* For distributed caches, multi-key or key-less operations are routed in\nround robin fashion.\n* For replicated/invalidated caches, all operations are routed in round robin\nfashion, regardless of whether they are key-based or multi-key/key-less.\n\nThe routing and failover is transparent to the user code, so the operations\nexecuted against in a cluster look exactly the same as in the previous code\nexamples.\n\nWhen a connection with a server breaks,\nincomplete operations are retried in other servers in the cluster.\n\nIf a server that has a client listener registered fails or leaves the cluster,\nthe client transparently migrates the listener registration to another node in the cluster.\nBy doing so, the client can continue receiving events in the presence of failures or topology changes.\n\nYou can run a test locally by starting multiple instances of Infinispan\nServer like this:\n\n    $ ./bin/server.sh -c infinispan.xml --node-name node0 -o 100\n    $ ./bin/server.sh -c infinispan.xml --node-name node1 -o 200\n    $ ./bin/server.sh -c infinispan.xml --node-name node2 -o 300\n\nAnd then using this code to verify that the topology is the expected one:\n\n#### Note for Mac Users:\nYou might experience MPING issues running an  Infinispan cluster.\n\n```bash\n13:37:15,561 ERROR (jgroups-5,server-two) [org.jgroups.protocols.MPING] \n```\n\nIf you run into the errors above, add the following to the routes of your host\n\n```bash\nsudo route add -net 224.0.0.0/5 127.0.0.1\nsudo route add -net 232.0.0.0/5 192.168.1.3\n```\n\n```Javascript\nvar infinispan = require('infinispan');\n\nvar connected = infinispan.client({port: 11322, host: '127.0.0.1'});\n\nconnected.then(function (client) {\n\n  var members = client.getTopologyInfo().getMembers();\n\n  // Should show all expected cluster members\n  console.log('Connected to: ' + JSON.stringify(members));\n\n  // Add your own operations here...\n\n  return client.disconnect();\n\n}).catch(function(error) {\n\n  console.log(\"Got error: \" + error.message);\n\n});\n```\n\n## Working with Sites\n\nMultiple Infinispan Server clusters can be deployed in such way that each cluster belongs to a different site.\nSuch deployments are done to enable data to be backed up from one cluster to another, potentially in a different geographical location.\nThis Javascript client implementation not only can failover between failures in nodes within a cluster, but if the entire cluster fails to respond, it can failover to a different cluster.\nIf the failover succeeds, the client will remain connected to the alternative cluster until this becomes unavailable, in which case it’ll try any other clusters defined, and ultimately, it’ll try the original server settings.\nTo be able to failover between clusters, first and foremost Infinispan Servers have to be [configured with cross-site replication](https://infinispan.org/docs/stable/titles/xsite/xsite.html).\nNext, the client has to provide alternative `clusters` configuration with at least one host/port pair details for each of the clusters configured.\nFor example:\n\n```Javascript\nvar connected = infinispan.client({port: 11322, host: '127.0.0.1'},\n  {\n    clusters: [\n      {\n        name: 'site-a',\n        servers: [{port: 1234, host: 'hostA1'}]\n      },\n      {\n        name: 'site-b',\n        servers: [{port: 2345, host: 'hostB1'}, {port: 3456, host: 'hostB2'}]\n      }\n    ]\n  });\n```\n\n### Manual Cluster Switch\n\nAs well as supporting automatic site cluster failover, Javascript clients can also switch between site clusters manually by calling `switchToCluster(clusterName)` and `switchToDefaultCluster()`.\nUsing `switchToCluster(clusterName)``, users can force a client to switch to one of the clusters pre-defined in the client configuration. To switch to the initial servers defined in the client configuration, call `switchToDefaultCluster()`.\nFor example:\n\n```Javascript\nvar connected = infinispan.client({port: 11322, host: '127.0.0.1'},\n  {\n    clusters: [\n      {\n        name: 'site-a',\n        servers: [{port: 1234, host: 'hostA1'}]\n      },\n      {\n        name: 'site-b',\n        servers: [{port: 2345, host: 'hostB1'}, {port: 3456, host: 'hostB2'}]\n      }\n    ]\n  });\n\nconnected.then(function (client) {\n\n  var switchToB = client.getTopologyInfo().switchToCluster('site-b');\n\n  switchToB.then(function(switchSucceed) {\n\n    if (switchSucceed) {\n      ...\n    }\n\n    ...\n\n    var switchToDefault = client.getTopologyInfo().switchToDefaultCluster();\n\n    switchToDefault.then(function(switchSucceed) {\n\n      if (switchSucceed) {\n        ...\n      }\n\n    })\n\n  })\n\n});\n```\n\n# Logging\n\nThe client uses [`log4js`](https://www.npmjs.com/package/log4js) for logging.\nTo configure it, simply create a JSON file with the desired configuration.\nHere is an example configuration that is used when running the client's testsuite:\n\n```json\n{\n  \"appenders\": {\n    \"test\": {\n      \"type\": \"fileSync\",\n      \"filename\": \"tmp-tests.log\"\n    }\n  },\n  \"categories\": {\n    \"default\": {\n      \"appenders\": [\"test\"],\n      \"level\": \"trace\"\n    }\n  }\n}\n```\n\nYou can find more examples [here](https://github.com/log4js-node/log4js-node/tree/master/examples).\n\nOnce you have the file, simply invoke `log4js` to use that file and then construct the client as usual, e.g.\n\n```js\nvar log4js = require('log4js');\nlog4js.configure('path/to/my-log4js.json');\n\n```\n\n# Async / Await\n\nExamples above can be greatly simplified taking advantage of `async` / `await` constructs,\nwhich are available in Node.js since version `7.10.0`.\n\nThis section shows how some of the examples above can be written using `async` / `await`:\n\n## Working with single entries and statistics\n\n```Javascript\nconst infinispan = require(\"infinispan\");\n\nconst log4js = require('log4js');\nlog4js.configure('example-log4js.json');\n\nasync function test() {\n  await new Promise((resolve, reject) => setTimeout(() => resolve(), 1000));\n  console.log('Hello, World!');\n\n  let client = await infinispan.client({port: 11222, host: '127.0.0.1'});\n  console.log(`Connected to Infinispan dashboard data`);\n\n  await client.put('key', 'value');\n\n  let value = await client.get('key');\n  console.log('get(key)=' + value);\n\n  let success = await client.remove('key');\n  console.log('remove(key)=' + success);\n\n  let stats = await client.stats();\n  console.log('Number of stores: ' + stats.stores);\n  console.log('Number of cache hits: ' + stats.hits);\n  console.log('All stats: ' + JSON.stringify(stats, null, \" \"));\n\n  await client.disconnect();\n}\n\ntest();\n```\n\n## Working with multiple entries\n\n```Javascript\nconst infinispan = require(\"infinispan\");\n\nconst log4js = require('log4js');\nlog4js.configure('example-log4js.json');\n\nasync function test() {\n  let client = await infinispan.client({port: 11222, host: '127.0.0.1'});\n  console.log(`Connected to Infinispan dashboard data`);\n\n  let data = [\n    {key: 'multi1', value: 'v1'},\n    {key: 'multi2', value: 'v2'},\n    {key: 'multi3', value: 'v3'}];\n\n  await client.putAll(data);\n\n  let entries = await client.getAll(['multi2', 'multi3']);\n  console.log('getAll(multi2, multi3)=%s', JSON.stringify(entries));\n\n  let iterator = await client.iterator(1);\n\n  let entry = {done: true};\n\n  do {\n    entry = await iterator.next();\n    console.log('iterator.next()=' + JSON.stringify(entry));\n  } while (!entry.done);\n\n  await iterator.close();\n\n  await client.clear();\n\n  await client.disconnect();\n}\n\ntest();\n```\n\n# Testing\n\nBefore executing any tests, Infinispan Server instances need to be started\nup so that testsuite can run against those. To ease this process, a script\nhas been created in the root directory to start all the expected server\ninstances.\n\nGo to the root of the repo and execute:\n\n```bash\n$ npm install\n```\n\nNext, start the Infinispan Servers via:\n\n```bash\n$ ./run-server.sh\n```\n\nTo run the testsuite once execute:\n\n```bash\n$ ./run-testsuite.sh\n```\n\nTo run tests continuously execute:\n\n```bash\n$ ./node_modules/.bin/jasmine-node spec --autotest --watch lib --captureExceptions\n```\n\nTo run individual tests execute:\n\n```bash\n$ node node_modules/jasmine-node/lib/jasmine-node/cli.js spec/infinispan_local_spec.js --captureExceptions\n```\n\nTo help with testing, you can quickly run the smoke tests via:\n\n```bash\n$ ./smoke-tests.sh\n```\n\nBoth testsuite and smoke tests can be run with older protocol versions, e.g.\n\n```bash\n$ protocol=2.5 ./smoke-tests.sh\n```\n\n# Manual stress tests\n\nThe testsuite now contains manual stress tests that take several minutes to run.\nTo run these tests, execute:\n\n    $ ./node_modules/.bin/jasmine-node spec-manual --captureExceptions\n\n\n# Memory profiling\n\nThe source code comes with some programs that allow the client's memory consumption to be profiled.\nThose programs rely on having access to the global garbage collector.\nSo, to run them you must pass `--expose-gc` command line parameter.\nExample:\n\n```bash\nnode --expose-gc memory-profiling/infinispan_memory_many_get.js\n```\n\nSo of programs might only report the memory usage before/after.\nOthers might generate heap dumps which can be visualized using Google Chrome.\nWithin Chrome, the Developer Tools UI contains a `Memory` tab where heap dumps can be loaded.\n\n\n# Debugging\n\nTo debug tests with IDE:\n\n    node --inspect-brk node_modules/jasmine-node/lib/jasmine-node/cli.js spec/codec_spec.js\n\nOr:\n\n    node --inspect-brk node_modules/jasmine-node/lib/jasmine-node/cli.js spec/infinispan_local_spec.js\n\nAnd then start a remote Node.js debugger from IDE on port 9229.\n\n# Tests, servers and ports\n\nHere's some more detailed information on which tests interact with which servers and on which ports.\nOn top of that, you can find information on which tests are always running as opposed to those that are started (and stopped) by the tests themselves.\n\n| Test          | Server Profile  | Ports (Auto/Manual)                     |\n| :------------ | :-------------: | :-------------------------------------- |\n| local spec    | local           | `11222` (A)                             |\n| expiry spec   | local           | `11222` (A)                             |\n| cluster spec  | clustered       | `11322` (A), `11332` (A), `11342` (A)   |\n| failover spec | clustered       | `11422` (M), `11432` (M), `11442` (M)   |\n| ssl spec      | local           | `11232` (A), `12242` (A), `12252` (A)   |\n| xsite spec    | earth, moon     | `11522` (earth, M), `11532` (moon, M)   |\n\n# Generating API documentation\n\nThe client contains JSDoc formatted API docs which can be generated via:\n\n    npm install jsdoc\n    ./node_modules/.bin/jsdoc lib/*.js\n    open out/index.html\n\n# Reporting an issue\n\nThis project does not use Github issues.\nInstead, please report them via JIRA (project [HRJS](https://issues.jboss.org/projects/HRJS/summary)).\n","readmeFilename":"README.md"}